Android BLE - Unable to Write to ESP32 - java

I have been trying to get BLE to work properly. I am needing to connect to an ESP32 and send wifi credentials for the device's initial setup. I have attempted to find code that functions and works correctly but have been unable to do so.
My overall goal is to have the user input the SSID and password, then the next screen called BLEScanner then has a scanner where the user will select the device and press connect. Once the device connects, I am trying to have the application handle the data transfer without the user needing to provide any more input. In the code below, the user inputs the wifi credentials in a previous view that passes the information to BLEScanner.
I am running the application on a LG Nexus 5. It is running Android 6.0.1
I am attempting to use the following code to write to a Gatt characteristic after I discover it. When I get the list, I am parsing through the characteristics that I have discovered and attempting to write to all of the characteristics. To write to the characteristics, I am doing the following. gattCharacteristic is type BluetoothGattCharacteristic. bluetoothGatt is of type BluetoothGatt. HomeBoyEugene is the SSID of a local coffee shop, which is why is is listed in the code at the end of the question.
gattCharacteristic.setValue("SSID_AS_STRING");
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
bluetoothGatt.writeCharacteristic(gattCharacteristic);
I have noticed some strange behavior while trying to get this to work correctly. The first is that when I perform a permissions check, ActivityCompat.checkSelfPermission(this, BLUETOOTH_CONNECT) does not return as PERMISSION_GRANTED. I have tried to include methods to grant the permissions but they are not working. I have also included the permission in my Manifest, using the following block.
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
This is the segment of code that I am using to try to get permissions for BLUETOOTH_CONNECT:
ActivityCompat.requestPermissions(BLEScanner.this, new String[] { BLUETOOTH_CONNECT }, 0);
Okay here's where we get into the confusing behavior. I'm using the following block to read the value of the characteristic and convert it into a string so I can print it into Logcat in Android Studio
byte[] hex2 = gattCharacteristic.getValue();
String str2 = new String(hex2, StandardCharsets.UTF_8);
When I perform this operation, it is returning the value that I am attempting to write to it, i.e. SSID_AS_STRING. However, the ESP32 is not recognizing a write event. The ESP will display on its console whenever a write event occurs and we have verified that the ESP code works with other third party apps. I am trying to figure out now why it would seem that I am writing to the characteristics on my end and that I am able to read the value that I am writing to it, but the ESP is not actually writing to it.
Below is a large snippet of the code. I have a lot of print statements throughout, but figured you would like to see everything in context.
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
String mWriteValue = "Testing";
Log.i("gattServices", String.valueOf(gattServices));
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
final String uuid = gattService.getUuid().toString();
System.out.println("Service discovered: " + uuid);
BLEScanner.this.runOnUiThread(new Runnable() {
public void run() {
Log.i("service", "Service discovered: " + uuid + "\n");
}
});
new ArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic> gattCharacteristics =
gattService.getCharacteristics();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic :
gattCharacteristics) {
final String charUuid = gattCharacteristic.getUuid().toString();
//System.out.println("Characteristic discovered for service: " + charUuid);
gattCharacteristic.setValue("HomeBoyEugene");
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
if (ActivityCompat.checkSelfPermission(this, BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
Log.i("Permission Check","Checking Permissions for writeCharacteristic");
ActivityCompat.requestPermissions(BLEScanner.this, new String[] { BLUETOOTH_CONNECT }, 0);
}
bluetoothGatt.writeCharacteristic(gattCharacteristic);
Log.i("characteristics", "Characteristic discovered for service: " + charUuid + "\n");
Log.i("read characteristics", String.valueOf(bluetoothGatt.readCharacteristic(gattCharacteristic)));
byte[] hex2 = gattCharacteristic.getValue();
String str2 = new String(hex2, StandardCharsets.UTF_8);
Log.i("value", String.valueOf(str2));
Log.i("spacer", "----------------------------------------");
}
Log.i("BREAK","=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
// Second loop to try to read what was written to the characteristics in the previous for loop
for (BluetoothGattCharacteristic gattCharacteristic :
gattCharacteristics) {
byte[] hex2 = gattCharacteristic.getValue();
String str2 = new String(hex2, StandardCharsets.UTF_8);
Log.i("value", String.valueOf(str2));
}
Log.i("BREAK","=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=/");
}
}
Any and all help is much appreciated. I don't really know how to word what I'm asking but I'm out of ideas on how to continue with debugging this. Thanks!

Related

How to refresh Bluetooth device name [Scan Cache]?

I am working on an application where I am connecting with the BLE device and sending commands.
One of that commands we have a command for changing the Bluetooth device name.
Communication is working fine, but the problem is when we send the command for changing the name it was working, BLE confirms the input and sends us the output, but when we disconnect and run LE Scan it was showing the same name as the previous, it should show the new name of the device.
If I want to get the latest name of the device I need to open the Bluetooth page manually in the device and scan over there in the scan result it was showing the latest name, when I open the app again which is in the background and its scanning under LE scan function with 10-sec delay, it was showing the new name in the list.
How can I ask Bluetooth manager or system to refresh the cache or refresh data for that Bluetooth device ?.
I don't know it was right to create ticket, but i have created ticket in google issue tracker : https://issuetracker.google.com/issues/233924346
Thanks.
I had the same problem and solved it by reading the new name from the raw scan data. In this way you never have to use device.getName() which returns the old name from the cache. This is Android Java code for the scan callback function.
private ScanCallback newscancallback()
{
ScanCallback scb;
// Device scan callback.
scb = new ScanCallback()
{
#Override
public void onScanResult(int callbackType, ScanResult result)
{
super.onScanResult(callbackType, result);
int n,k,len,getout;
BluetoothDevice dev;
byte[] rec;
StringBuilder nameb;
String name;
dev = result.getDevice();
// do not use dev.getName() which returns cached name
// read current name from raw scan record instead
name = null;
rec = result.getScanRecord().getBytes();
len = rec.length;
nameb = new StringBuilder();
n = 0;
getout = 0;
// search scan record for name
while(n < len-2 && rec[n] != 0 && getout == 0)
{
// rec[n] is length of next item
// rec[n+1] is item type - look for 8 or 9=name
// rec[n+2].. is the name, length rec[n]-1
if(rec[n] > 1 && (rec[n+1] == 8 || rec[n+1] == 9)
{ // found name
for(k = 0 ; k < rec[n]-1 ; ++k)
nameb.append((char)rec[n+2+k]);
name = nameb.toString();
getout = 1;
}
else // go to next item
n += rec[n] + 1;
}
// name is now null or the new name from the scan record
}
#Override
public void onScanFailed(int errcode)
{
}
#Override
public void onBatchScanResults(List<ScanResult> result)
{
}
};
return (scb);
}
As you can see the latest name in the Bluetooth settings of the mobile device, I believe there is no issue with the Bluetooth manager of the system. The issue will be in the scanning function of the code as it is not actually refreshing the scan list yet and it might saved the last known BLE list somewhere in the cache. If you are using third-party library, you might need to check their documentation or codes about how the scan function actually works. There may be like force-refresh option or something in the library. As far as I know, to save the device's battery, there is a delay to actually refresh the scan list.

No IMEI for Android Developers in Android 10

As Android is serious about security and trying to make new android versions more secure, its becoming tough for developers to keep up-to date with new security features and find old methods alternatives to make their app compatible with old features.
This question is about IMEI in New Android 10!
The old method was super easy to get IMEI number by using below code
String deviceId = "";
if (Build.VERSION.SDK_INT >= 26) {
if (telMgr.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
deviceId = telMgr.getMeid();
} else if (telMgr.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
deviceId = telMgr.getImei();
} else {
deviceId = ""; // default!!!
}
} else {
deviceId = telMgr.getDeviceId();
}
In New Android 10 it is now restricted to get IMEI number. According to android documentation
apps must have the READ_PRIVILEGED_PHONE_STATE privileged permission in order to access the device's non-resettable identifiers, which include both IMEI and serial number.
The problem is that when we try to ask run time permissions with
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
My compiler does not recognize this permissions and i got error on this line but when i ask this permission in manifest file it does recognize this line but through warning that this permission is only for system apps.
I want to make my app compatible with Android 10 and want to get IMEI. How can i get IMEI number in Android 10 without becoming device owner or profile owner ?
Only device owner apps can read unique identifiers or your app must be a system app with READ_PRIVILEGED_PHONE_STATE. You can't ask this permission for a normal app.
Android 10 Restricted developer to Access IMEI number.
You can have a alternate solution by get Software ID. You can use software id as a unique id. Please find below code as i use in Application.
public static String getDeviceId(Context context) {
String deviceId;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
deviceId = Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID);
}else {
final TelephonyManager mTelephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId();
} else {
deviceId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID);
}
}
return deviceId;
}
To get a unique identifier you can use FirebaseInstanceId.getInstance().getId(); or String uniqueID = UUID.randomUUID().toString();. Have a look here for best practices https://developer.android.com/training/articles/user-data-ids#java

Get Mobile number from user device in android not using TeliphonecManager class TelephonyManager [duplicate]

How can I programmatically get the phone number of the device that is running my android app?
Code:
TelephonyManager tMgr = (TelephonyManager)mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
String mPhoneNumber = tMgr.getLine1Number();
Required Permission:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Caveats:
According to the highly upvoted comments, there are a few caveats to be aware of. This can return null or "" or even "???????", and it can return a stale phone number that is no longer valid. If you want something that uniquely identifies the device, you should use getDeviceId() instead.
There is no guaranteed solution to this problem because the phone number is not physically stored on all SIM-cards, or broadcasted from the network to the phone. This is especially true in some countries which requires physical address verification, with number assignment only happening afterwards. Phone number assignment happens on the network - and can be changed without changing the SIM card or device (e.g. this is how porting is supported).
I know it is pain, but most likely the best solution is just to ask the user to enter his/her phone number once and store it.
Update: This answer is no longer available as Whatsapp had stopped exposing the phone number as account name, kindly disregard this answer.
There is actually an alternative solution you might want to consider, if you can't get it through telephony service.
As of today, you can rely on another big application Whatsapp, using AccountManager. Millions of devices have this application installed and if you can't get the phone number via TelephonyManager, you may give this a shot.
Permission:
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
Code:
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccounts();
for (Account ac : accounts) {
String acname = ac.name;
String actype = ac.type;
// Take your time to look at all available accounts
System.out.println("Accounts : " + acname + ", " + actype);
}
Check actype for WhatsApp account
if(actype.equals("com.whatsapp")){
String phoneNumber = ac.name;
}
Of course you may not get it if user did not install WhatsApp, but its worth to try anyway.
And remember you should always ask user for confirmation.
So that's how you request a phone number through the Play Services API without the permission and hacks. Source and Full example.
In your build.gradle (version 10.2.x and higher required):
compile "com.google.android.gms:play-services-auth:$gms_version"
In your activity (the code is simplified):
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Auth.CREDENTIALS_API)
.build();
requestPhoneNumber(result -> {
phoneET.setText(result);
});
}
public void requestPhoneNumber(SimpleCallback<String> callback) {
phoneNumberCallback = callback;
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest);
try {
startIntentSenderForResult(intent.getIntentSender(), PHONE_NUMBER_RC, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Logs.e(TAG, "Could not start hint picker Intent", e);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PHONE_NUMBER_RC) {
if (resultCode == RESULT_OK) {
Credential cred = data.getParcelableExtra(Credential.EXTRA_KEY);
if (phoneNumberCallback != null){
phoneNumberCallback.onSuccess(cred.getId());
}
}
phoneNumberCallback = null;
}
}
This will generate a dialog like this:
As posted in my earlier answer
Use below code :
TelephonyManager tMgr = (TelephonyManager)mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
String mPhoneNumber = tMgr.getLine1Number();
In AndroidManifest.xml, give the following permission:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
But remember, this code does not always work, since Cell phone number is dependent on the SIM Card and the Network operator / Cell phone carrier.
Also, try checking in Phone--> Settings --> About --> Phone Identity, If you are able to view the Number there, the probability of getting the phone number from above code is higher. If you are not able to view the phone number in the settings, then you won't be able to get via this code!
Suggested Workaround:
Get the user's phone number as manual input from the user.
Send a code to the user's mobile number via SMS.
Ask user to enter the code to confirm the phone number.
Save the number in sharedpreference.
Do the above 4 steps as one time activity during the app's first launch. Later on, whenever phone number is required, use the value available in shared preference.
There is a new Android api that allows the user to select their phonenumber without the need for a permission. Take a look at:
https://android-developers.googleblog.com/2017/10/effective-phone-number-verification.html
// Construct a request for phone numbers and show the picker
private void requestHint() {
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(
apiClient, hintRequest);
startIntentSenderForResult(intent.getIntentSender(),
RESOLVE_HINT, null, 0, 0, 0);
}
private String getMyPhoneNumber(){
TelephonyManager mTelephonyMgr;
mTelephonyMgr = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
return mTelephonyMgr.getLine1Number();
}
private String getMy10DigitPhoneNumber(){
String s = getMyPhoneNumber();
return s != null && s.length() > 2 ? s.substring(2) : null;
}
Code taken from http://www.androidsnippets.com/get-my-phone-number
Just want to add a bit here to above explanations in the above answers. Which will save time for others as well.
In my case this method didn't returned any mobile number, an empty string was returned. It was due to the case that I had ported my number on the new sim. So if I go into the Settings>About Phone>Status>My Phone Number it shows me "Unknown".
Sometimes, below code returns null or blank string.
TelephonyManager tMgr = (TelephonyManager)mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
String mPhoneNumber = tMgr.getLine1Number();
With below permission
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
There is another way you will be able to get your phone number, I haven't tested this on multiple devices but above code is not working every time.
Try below code:
String main_data[] = {"data1", "is_primary", "data3", "data2", "data1", "is_primary", "photo_uri", "mimetype"};
Object object = getContentResolver().query(Uri.withAppendedPath(android.provider.ContactsContract.Profile.CONTENT_URI, "data"),
main_data, "mimetype=?",
new String[]{"vnd.android.cursor.item/phone_v2"},
"is_primary DESC");
if (object != null) {
do {
if (!((Cursor) (object)).moveToNext())
break;
// This is the phoneNumber
String s1 = ((Cursor) (object)).getString(4);
} while (true);
((Cursor) (object)).close();
}
You will need to add these two permissions.
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
Hope this helps,
Thanks!
First of all getting users mobile number is against the Ethical policy, earlier it was possible but now as per my research there no solid solution available for this, By using some code it is possible to get mobile number but no guarantee may be it will work only in few device. After lot of research i found only three solution but they are not working in all device.
There is the following reason why we are not getting.
1.Android device and new Sim Card not storing mobile number if mobile number is not available in device and in sim then how it is possible to get number, if any old sim card having mobile number then using Telephony manager we can get the number other wise it will return the “null” or “” or “??????”
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
TelephonyManager tel= (TelephonyManager)this.getSystemService(Context.
TELEPHONY_SERVICE);
String PhoneNumber = tel.getLine1Number();
Note:- I have tested this solution in following device Moto x, Samsung Tab 4, Samsung S4, Nexus 5 and Redmi 2 prime but it doesn’t work every
time it return empty string so conclusion is it's useless
This method is working only in Redmi 2 prime, but for this need to add
read contact permission in manifest.
Note:- This is also not the guaranteed and efficient solution, I have tested this solution in many device but it worked only in Redmi 2 prime
which is dual sim device it gives me two mobile number first one is
correct but the second one is not belong to my second sim it belong to
my some old sim card which i am not using.
String main_data[] = {"data1", "is_primary", "data3", "data2", "data1",
"is_primary", "photo_uri", "mimetype"};
Object object = getContentResolver().
query(Uri.withAppendedPath(android.provider.ContactsContract.Profile.CONTENT_URI, "data"),
main_data, "mimetype=?",
new String[]{"vnd.android.cursor.item/phone_v2"},
"is_primary DESC");
String s1="";
if (object != null) {
do {
if (!((Cursor) (object)).moveToNext())
break;
// This is the phoneNumber
s1 =s1+"---"+ ((Cursor) (object)).getString(4);
} while (true);
((Cursor) (object)).close();
}
In my research i have found earlier it was possible to get mobile number using WhatsApp account but now new Whatsapp version doesn’t storing user's mobile number.
Conclusion:- Android doesn’t have any guaranteed solution to get
user's mobile number programmatically.
Suggestion:- 1. If you want to verify user’s mobile number then ask to
user to provide his number, using otp you can can verify that.
If you want to identify the user’s device, for this you can easily get device IMEI number.
TelephonyManager is not the right solution, because in some cases the number is not stored in the SIM. I suggest that you should use the shared preference to store the user's phone number for the first time the application is open and the number will used whenever you need.
This is a more simplified answer:
public String getMyPhoneNumber()
{
return ((TelephonyManager) getSystemService(TELEPHONY_SERVICE))
.getLine1Number();
}
Here's a combination of the solutions I've found (sample project here, if you want to also check auto-fill):
manifest
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
build.gradle
implementation "com.google.android.gms:play-services-auth:17.0.0"
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var googleApiClient: GoogleApiClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tryGetCurrentUserPhoneNumber(this)
googleApiClient = GoogleApiClient.Builder(this).addApi(Auth.CREDENTIALS_API).build()
if (phoneNumber.isEmpty()) {
val hintRequest = HintRequest.Builder().setPhoneNumberIdentifierSupported(true).build()
val intent = Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest)
try {
startIntentSenderForResult(intent.intentSender, REQUEST_PHONE_NUMBER, null, 0, 0, 0);
} catch (e: IntentSender.SendIntentException) {
Toast.makeText(this, "failed to show phone picker", Toast.LENGTH_SHORT).show()
}
} else
onGotPhoneNumberToSendTo()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_PHONE_NUMBER) {
if (resultCode == Activity.RESULT_OK) {
val cred: Credential? = data?.getParcelableExtra(Credential.EXTRA_KEY)
phoneNumber = cred?.id ?: ""
if (phoneNumber.isEmpty())
Toast.makeText(this, "failed to get phone number", Toast.LENGTH_SHORT).show()
else
onGotPhoneNumberToSendTo()
}
}
}
private fun onGotPhoneNumberToSendTo() {
Toast.makeText(this, "got number:$phoneNumber", Toast.LENGTH_SHORT).show()
}
companion object {
private const val REQUEST_PHONE_NUMBER = 1
private var phoneNumber = ""
#SuppressLint("MissingPermission", "HardwareIds")
private fun tryGetCurrentUserPhoneNumber(context: Context): String {
if (phoneNumber.isNotEmpty())
return phoneNumber
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val subscriptionManager = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
try {
subscriptionManager.activeSubscriptionInfoList?.forEach {
val number: String? = it.number
if (!number.isNullOrBlank()) {
phoneNumber = number
return number
}
}
} catch (ignored: Exception) {
}
}
try {
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val number = telephonyManager.line1Number ?: ""
if (!number.isBlank()) {
phoneNumber = number
return number
}
} catch (e: Exception) {
}
return ""
}
}
}
Add this dependency:
implementation 'com.google.android.gms:play-services-auth:18.0.0'
To fetch phone number list use this:
val hintRequest = HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build()
val intent = Credentials.getClient(context).getHintPickerIntent(hintRequest)
startIntentSenderForResult(
intent.intentSender,
PHONE_NUMBER_FETCH_REQUEST_CODE,
null,
0,
0,
0,
null
)
After tap on play services dialog:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent? {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PHONE_NUMBER_FETCH_REQUEST_CODE) {
data?.getParcelableExtra<Credential>(Credential.EXTRA_KEY)?.id?.let {
useFetchedPhoneNumber(it)
}
}
}
A little contribution. In my case, the code launched an error exception. I have needed put an annotation that for the code be run and fix that problem. Here I let this code.
public static String getLineNumberPhone(Context scenario) {
TelephonyManager tMgr = (TelephonyManager) scenario.getSystemService(Context.TELEPHONY_SERVICE);
#SuppressLint("MissingPermission") String mPhoneNumber = tMgr.getLine1Number();
return mPhoneNumber;
}
For android version >= LOLLIPOP_MR1 :
Add permission :
And call this :
val subscriptionManager =
getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
val list = subscriptionManager.activeSubscriptionInfoList
for (info in list) {
Log.d(TAG, "number " + info.number)
Log.d(TAG, "network name : " + info.carrierName)
Log.d(TAG, "country iso " + info.countryIso)
}
}
I noticed several answers posting the same thing. First of all things changed as per 2021, onActivityResult is deprecated. Here is the non-deprecated solution.
private fun requestHint() {
val hintRequest = HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build()
val intent = Credentials.getClient(this).getHintPickerIntent(hintRequest)
val intentSender = IntentSenderRequest.Builder(intent.intentSender).build()
val resultLauncher = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val credential: Credential? = result.data?.getParcelableExtra(Credential.EXTRA_KEY)
// Phone number with country code
Log.i("mTag", "Selected phone No: ${credential?.id}")
}
}
resultLauncher.launch(intentSender)
}
Note: While many of you think this allows you to retrieve user's mobile phone number. That is usually not the case. Google Play Services has cached few phone numbers and sometimes the dialog shows phone numbers in which none belongs to user.
An important import com.google.android.gms.auth.api.credentials.Credential
Reference Documentation provides details but the code is somewhat deprecated.
Although it's possible to have multiple voicemail accounts, when calling from your own number, carriers route you to voicemail. So, TelephonyManager.getVoiceMailNumber() or TelephonyManager.getCompleteVoiceMailNumber(), depending on the flavor you need.
Hope this helps.
Wouldn't be recommending to use TelephonyManager as it requires the app to require READ_PHONE_STATE permission during runtime.
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Should be using Google's Play Service for Authentication, and it will able to allow User to select which phoneNumber to use, and handles multiple SIM cards, rather than us trying to guess which one is the primary SIM Card.
implementation "com.google.android.gms:play-services-auth:$play_service_auth_version"
fun main() {
val googleApiClient = GoogleApiClient.Builder(context)
.addApi(Auth.CREDENTIALS_API).build()
val hintRequest = HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build()
val hintPickerIntent = Auth.CredentialsApi.getHintPickerIntent(
googleApiClient, hintRequest
)
startIntentSenderForResult(
hintPickerIntent.intentSender, REQUEST_PHONE_NUMBER, null, 0, 0, 0
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_PHONE_NUMBER -> {
if (requestCode == Activity.RESULT_OK) {
val credential = data?.getParcelableExtra<Credential>(Credential.EXTRA_KEY)
val selectedPhoneNumber = credential?.id
}
}
}
}
If I'm getting number from voiceMailNumer then it is working good -
val telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
) {
Log.d("number", telephonyManager.voiceMailNumber.toString())
}
Firstly Initalize your sign in Intent like this
private val signInIntent = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
try {
val phoneNumber = Identity.getSignInClient(requireContext()).getPhoneNumberFromIntent(result.data)
// Note phone number will be in country code + phone number format
} catch (e: Exception) {
}
}
To open google play intent and show phone number associated with google account use this
val phoneNumberHintIntentRequest = GetPhoneNumberHintIntentRequest.builder()
.build()
Identity.getSignInClient(requireContext())
.getPhoneNumberHintIntent(phoneNumberHintIntentRequest)
.addOnSuccessListener { pendingIntent ->
signInIntent.launch(IntentSenderRequest.Builder(pendingIntent).build())
}.addOnFailureListener {
it.printStackTrace()
}
Note:
This will fail if user is disabled phone number sharing. If is it so user have to enable that from Settings -> Google -> Auto-fill -> Phone Number sharing
This will not working if you are using emulated device where play services is not available
while working on a security app which needed to get the phone number of who so ever my phone might get into their hands, I had to do this;
1. receive Boot completed and then try getting Line1_Number from telephonyManager which returns a string result.
2. compare the String result with my own phone number and if they don't match or string returns null then,
3. secretly send an SMS containing the string result plus a special sign to my office number.
4. if message sending fails, start a service and keep trying after each hour until sent SMS pending intent returns successful.
With this steps I could get the number of the person using my lost phone.
it doesn't matter if the person is charged.

Change the Android bluetooth device name

I know it's possible to get the local device name as described in the solution to this question Display Android Bluetooth Device Name
What I'm interested in knowing is, can I change the local buetooth name (the one other devices see when I'm in discovery mode) programaticlly. I know you can change it by hand, but I'm writing and app and I want to be able to change the name (add a simple flag) so other devices with the same application can scan and instantly know if the phone is also running the app.
tl;dr: How can I change the bluetooth device name on android?
Yes you can change your device name using setName(String name) of BluetoothAdapter type.Following is the sample code:
private BluetoothAdapter bluetoothAdapter = null;
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
void ChangeDeviceName(){
Log.i(LOG, "localdevicename : "+bluetoothAdapter.getName()+" localdeviceAddress : "+bluetoothAdapter.getAddress());
bluetoothAdapter.setName("NewDeviceName");
Log.i(LOG, "localdevicename : "+bluetoothAdapter.getName()+" localdeviceAddress : "+bluetoothAdapter.getAddress());
}
Thanks for the original answer, here are a few things I found when implementing that might help someone else out.
1) BT has to be enabled for setName() to work.
2) It takes time for BT to Enable. ie. you Can't just call enable() then setName()
3) It takes time for the name to "sink in". ie. you can't call getName() right after setName() and expect the new name.
So, here is a snippet of code I came up with to use a runnable to get the job done in the background. It is also time bound to 10seconds, so it won't run forever if there is a problem.
Finally, this is part of our power on check, and we normally leave BT disabled (due to battery). So, I turn BT back off after, you may not want to do that.
// BT Rename
//
final String sNewName = "Syntactics";
final BluetoothAdapter myBTAdapter = BluetoothAdapter.getDefaultAdapter();
final long lTimeToGiveUp_ms = System.currentTimeMillis() + 10000;
if (myBTAdapter != null)
{
String sOldName = myBTAdapter.getName();
if (sOldName.equalsIgnoreCase(sNewName) == false)
{
final Handler myTimerHandler = new Handler();
myBTAdapter.enable();
myTimerHandler.postDelayed(
new Runnable()
{
#Override
public void run()
{
if (myBTAdapter.isEnabled())
{
myBTAdapter.setName(sNewName);
if (sNewName.equalsIgnoreCase(myBTAdapter.getName()))
{
Log.i(TAG_MODULE, "Updated BT Name to " + myBTAdapter.getName());
myBTAdapter.disable();
}
}
if ((sNewName.equalsIgnoreCase(myBTAdapter.getName()) == false) && (System.currentTimeMillis() < lTimeToGiveUp_ms))
{
myTimerHandler.postDelayed(this, 500);
if (myBTAdapter.isEnabled())
Log.i(TAG_MODULE, "Update BT Name: waiting on BT Enable");
else
Log.i(TAG_MODULE, "Update BT Name: waiting for Name (" + sNewName + ") to set in");
}
}
} , 500);
}
}
To change the bluetooth name properly you need to take care of following things:
1) You need following permissions:
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
2) Check the bluetooth state from adapter as you can only change the name of bluetooth is turned on.
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter.state == BluetoothAdapter.STATE_ON){
bluetoothAdapter.setName("NewDeviceName");
}
3) If the bluetooth is not turned on then you can turn it on with the following command:
bluetoothAdapter.enable()
4) Last thing, please don't use static timers to wait for bluetooth state changes instead the proper way is that you can register for android.bluetooth.adapter.action.STATE_CHANGED broadcast and useBluetoothAdapter.EXTRA_STATE to get the new state of bluetooth whenever it is changed.
Note: Not all devices behave the same when it comes to bluetooth and changing the name due to caching and hw address, so never expect same outcome from all devices.

How to obtain MAC address of WiFi network interface?

It seems the java.net.NetworkInterface implementation of android does not have a
byte[] getHardwareAddress() method
http://developer.android.com/reference/java/net/NetworkInterface.html
I've found several forums of people trying to do this with no definitive answer, I need to get a somewhat cross-device UUID, so I can't rely on phone numbers or in ANDROID_ID (which can be overwritten and which I think depends on the user having a google account)
http://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID
In linux you can use ifconfig or read from /proc/net/arp and you can easily get the Hardware address.
Is there a file in android that I can read?
There has to be a way to get this address since it's shown in the "Settings > About Phone > Status" of the phone.
Late answer, but it can help others with the same "problem".
The answer is really straight forward:
WifiManager wifiMan = (WifiManager) this.getSystemService(
Context.WIFI_SERVICE);
WifiInfo wifiInf = wifiMan.getConnectionInfo();
String macAddr = wifiInf.getMacAddress();
The above code will get you the MAC address of your device, remember to have wifi enabled when grabbing the address. This code snippet should be used in your Activity.
There has to be a way to get this
address since it's shown in the
"Settings > About Phone > Status" of
the phone.
Which means, if nothing else, you can go putter around the Android open source code, perhaps using Google Code Search, to figure out where it pulls that from.
Doing a bit of puttering myself, it would appear it is using getMacAddress() from WifiInfo.
UPDATE:
Beginning Android 6.0, above API will give you constant MAC address for all the devices, which is 02:00:00:00:00:00. Refer below for detailshttp://developer.android.com/about/versions/marshmallow/android-6.0-changes.html Found another post that claims to find MAC address in 6.0, not tested it thoughHow to get Wi-Fi Mac address in Android Marshmallow
On Android Q, there is no way to access mac address anymore.
WifiInfo.getMacAddress() will always return 02:00:00:00:00:00.
And WifiConfiguration.getRandomizedMacAddress() will not available anymore.
Add Following Permission.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
WifiManager initialize in onCreate.
WifiManager wifiMgr = (WifiManager) getContext().getSystemService(context.WIFI_SERVICE);
Use following function.
public void WI-FI_MAC() {
WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
String macAddress = wifiInfo.getMacAddress();
}
this my code and work well in android 5 +.
public static String getMacAddress() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
// res1.append(Integer.toHexString(b & 0xFF) + ":");
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
//handle exception
}
return "";
}

Categories