Issues with hidden API's after updating Android security patch (June 2022) - android

We have created a Bluetooth application to test different BT profiles. We ran into an issue after an Android update (This is not Android version specific. Issue is seen on both Android 11 and 12. Issue is 100 % reproducible if the security patch date >= June 2022. Do find below the code which was working fine without any problems but now fails with the error, System.err: Caused by: java.lang.SecurityException: Need BLUETOOTH PRIVILEGED permission: Neither user 10243 nor current process has android.permission.BLUETOOTH_PRIVILEGED
This permission android.permission.BLUETOOTH_PRIVILEGED is only applicable for system apps. Seems like Android security update is now restricting usage of some hidden API's on user apps and would only be usable on system apps. There seems to be no other alternative public api for the below use. Can somebody help in this regard in case you had a similar use case and somehow resolved this?
public static void changeScanMode(String cmdSuffix, String strCmdName, BluetoothAdapter mBluetoothAdapter) {
String scanMode = cmdSuffix;
if (scanMode.equalsIgnoreCase("scan_connectable_on")) {
boolean result = setBluetoothScanMode(mBluetoothAdapter, SCAN_MODE_CONNECTABLE);
Log.v(TAG, "set scan mode connectable result " + result);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(MainActivity.context, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
Log.e(TAG, "Permission not granted");
return;
}
if (mBluetoothAdapter.getScanMode() == SCAN_MODE_CONNECTABLE) {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [PASS] scanMode: SCAN_MODE_CONNECTABLE");
} else {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [FAIL] scanMode: SCAN_MODE_CONNECTABLE");
}
} else if (scanMode.equalsIgnoreCase("scan_connectable_discoverable_on")) {
boolean result = setBluetoothScanMode(mBluetoothAdapter, SCAN_MODE_CONNECTABLE_DISCOVERABLE);
Log.v(TAG, "set scan mode connectable discoverable result " + result);
if (mBluetoothAdapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [PASS] scanMode: SCAN_MODE_CONNECTABLE_DISCOVERABLE");
} else {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [FAIL] scanMode: SCAN_MODE_CONNECTABLE_DISCOVERABLE");
}
} else if (scanMode.equalsIgnoreCase("scan_mode_none")) {
boolean result = setBluetoothScanMode(mBluetoothAdapter, SCAN_MODE_NONE);
Log.v(TAG, "set scan mode none result " + result);
if (mBluetoothAdapter.getScanMode() == SCAN_MODE_NONE) {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [PASS] scanMode: SCAN_MODE_NONE");
} else {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [FAIL] scanMode: SCAN_MODE_NONE");
}
} else {
Log.v(TAG, "[" + strCmdName.toUpperCase() + "] [FAIL] scanMode: Enter Valid Scan Mode");
}
}

Related

WebRTC - Echo issue in Multiple calls on android devices

I am working on an Android app that allows live chat and call functionality. I am new to WebRTC in android. I am trying to add multiple call functionality using WebRTC. I got success in connecting multiple P2P calls (Upto 6 users are easily gets connected using Mesh Topology.
Here are the steps that I am following:
A => B Call successful ==> Result: audio clear no problem on both the ends
A => C Adding New Caller C from A ==> Result: audio clear no problem on both the ends.
C => B in background C gives call to B and gets accepted on B's end => Result: audio clear no problem on all the ends.
Now, All 3 participants are connected and can communicate easily.
The issue is:
When any of the participants leaves the call, Any of the remaining participants are hearing Echo of their own voice.
All my call related setups are done using RingRTC. Please help if anyone has faced this issue.
I tried setting up Noisce Supressors, AcousticEchoCanceler and other options for each remaining audio sessions as below. But its not helping.
public void enable(int audioSession) {
Logging.d(TAG, "enable(audioSession=" + audioSession + ")");
assertTrue(aec == null);
assertTrue(agc == null);
assertTrue(ns == null);
// Add logging of supported effects but filter out "VoIP effects", i.e.,
// AEC, AEC and NS.
for (Descriptor d : AudioEffect.queryEffects()) {
if (effectTypeIsVoIP(d.type) || DEBUG) {
Logging.d(TAG, "name: " + d.name + ", "
+ "mode: " + d.connectMode + ", "
+ "implementor: " + d.implementor + ", "
+ "UUID: " + d.uuid);
}
}
if (isAcousticEchoCancelerSupported()) {
// Create an AcousticEchoCanceler and attach it to the AudioRecord on
// the specified audio session.
aec = AcousticEchoCanceler.create(audioSession);
if (aec != null) {
boolean enabled = aec.getEnabled();
boolean enable = shouldEnableAec && canUseAcousticEchoCanceler();
if (aec.setEnabled(enable) != AudioEffect.SUCCESS) {
Logging.e(TAG, "Failed to set the AcousticEchoCanceler state");
}
Logging.d(TAG, "AcousticEchoCanceler: was "
+ (enabled ? "enabled" : "disabled")
+ ", enable: " + enable + ", is now: "
+ (aec.getEnabled() ? "enabled" : "disabled"));
} else {
Logging.e(TAG, "Failed to create the AcousticEchoCanceler instance");
}
}
if (isAutomaticGainControlSupported()) {
// Create an AutomaticGainControl and attach it to the AudioRecord on
// the specified audio session.
agc = AutomaticGainControl.create(audioSession);
if (agc != null) {
boolean enabled = agc.getEnabled();
boolean enable = shouldEnableAgc && canUseAutomaticGainControl();
if (agc.setEnabled(enable) != AudioEffect.SUCCESS) {
Logging.e(TAG, "Failed to set the AutomaticGainControl state");
}
Logging.d(TAG, "AutomaticGainControl: was "
+ (enabled ? "enabled" : "disabled")
+ ", enable: " + enable + ", is now: "
+ (agc.getEnabled() ? "enabled" : "disabled"));
} else {
Logging.e(TAG, "Failed to create the AutomaticGainControl instance");
}
}
if (isNoiseSuppressorSupported()) {
// Create an NoiseSuppressor and attach it to the AudioRecord on the
// specified audio session.
ns = NoiseSuppressor.create(audioSession);
if (ns != null) {
boolean enabled = ns.getEnabled();
boolean enable = shouldEnableNs && canUseNoiseSuppressor();
if (ns.setEnabled(enable) != AudioEffect.SUCCESS) {
Logging.e(TAG, "Failed to set the NoiseSuppressor state");
}
Logging.d(TAG, "NoiseSuppressor: was "
+ (enabled ? "enabled" : "disabled")
+ ", enable: " + enable + ", is now: "
+ (ns.getEnabled() ? "enabled" : "disabled"));
} else {
Logging.e(TAG, "Failed to create the NoiseSuppressor instance");
}
}
}

Amplify.Storage: Is there a way to list only the files in my bucket?

I am trying to list all the files in my bucket via:
Amplify.Storage.list("",
result -> {
for (StorageItem item : result.getItems()) {
Log.i("MyAmplifyApp", "File: " + item.getKey() + ", Hash: " + item.getETag());
}
},
error -> Log.e("MyAmplifyApp", "List failure", error)
);
But I am only interested in files, not directories. How do I do that?
I was not able to find a definite solution to my problem. Here is the closest solution I wrote:
Amplify.Storage.list("",
result -> {
for (StorageItem item : result.getItems()) {
if(item.getSize() == 0) {
Log.i(TAG, "File is empty, skip: " + item.getKey());
continue;
}
Log.i("MyAmplifyApp", "File: " + item.getKey() + ", Hash: " + item.getETag());
}
}
error -> Log.e("MyAmplifyApp", "List failure", error)
);
This simply ignores any StorageItem that is zero bytes in size. This works in my case because aside from directories, I am also not interested in empty files.

How was the source code of startService() modified to recognize if it was called from background?

Since about Android 9 it throws an IllegalStateException if startService() was called from the background. I see this exception many times in my developer console:
java.lang.IllegalStateException:
at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1666)
at android.app.ContextImpl.startService (ContextImpl.java:1611)
In these cases, Google recommends to call startForegroundService() and within 5 seconds startForeground(), instead. See "Background execution limits".
Anyway, calling startService() from foreground is perfectly ok. Now, I wonder how exactly Android recognizes/decides that an app is in the foreground to not wrongly throwing an IllegalStateException?
I was starting to dig the source code of Android9/10 and comparing it with 8/7 to discover how startService() was modified to recognize if it was called from foreground/background. But I'm convinced that many developers before me did this already, and I would be happy if they'd give an answer.
In AOSP10 (10.0.0_r25):
Server side:
in startServiceLocked from frameworks\base\services\core\java\com\android\server\am\ActiveServices.java:
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.shortInstanceName
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage + " startFg?=" + fgRequired);
......
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
Then in client side:
in ContextImpl.java as your log:
else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
Following this link into Android's soure code we find getAppStartModeLocked():
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
UidRecord uidRec = mActiveUids.get(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
+ (uidRec != null ? uidRec.idle : false));
if (uidRec == null || alwaysRestrict || uidRec.idle) {
boolean ephemeral;
if (uidRec == null) {
ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
UserHandle.getUserId(uid), packageName);
} else {
ephemeral = uidRec.ephemeral;
}
if (ephemeral) {
// We are hard-core about ephemeral apps not running in the background.
return ActivityManager.APP_START_MODE_DISABLED;
} else {
if (disabledOnly) {
// The caller is only interested in whether app starts are completely
// disabled for the given package (that is, it is an instant app). So
// we don't need to go further, which is all just seeing if we should
// apply a "delayed" mode for a regular app.
return ActivityManager.APP_START_MODE_NORMAL;
}
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid
+ " pkg=" + packageName + " startMode=" + startMode
+ " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid));
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
// This is an old app that has been forced into a "compatible as possible"
// mode of background check. To increase compatibility, we will allow other
// foreground apps to cause its services to start.
if (callingPid >= 0) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(callingPid);
}
if (proc != null &&
!ActivityManager.isProcStateBackground(proc.curProcState)) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
return ActivityManager.APP_START_MODE_NORMAL;
}
}
}
return startMode;
}
}
return ActivityManager.APP_START_MODE_NORMAL;
}
And the method appRestrictedInBackgroundLocked() (which is also called from appServicesRestrictedInBackgroundLocked() as fallback) decides about the startMode:
// Unified app-op and target sdk check
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
}
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
// ...and legacy apps get an AppOp check
int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
uid, packageName);
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
}
switch (appop) {
case AppOpsManager.MODE_ALLOWED:
return ActivityManager.APP_START_MODE_NORMAL;
case AppOpsManager.MODE_IGNORED:
return ActivityManager.APP_START_MODE_DELAYED;
default:
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
}
But the final decision about foreground or background is done in ActivityManager.isProcStateBackground(uidRec.setProcState):
/** #hide Should this process state be considered a background state? */
public static final boolean isProcStateBackground(int procState) {
return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
}
So, this section of the first method here gets the current state of foreground or background:
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(callingPid);
}
When the app is in following case, it will be allowed to started from background.
The app is persistent. Only the prebuilt system app can do this.
The app is not idle. When the app process is in background for a certain time, it will be set idle.
The app is idle but in an whitelist. backgroundWhitelistUid. Only the app with system uid can add apps to this list.

"GoogleApiClient has an optional Plus.API and is not connected to Plus"

I am using google login for my android app, off lately i am getting Runtime exception reports with error "GoogleApiClient has an optional Plus.API and is not connected to Plus. Use GoogleApiClient.hasConnectedApi(Plus.API) to guard this call."
In activity result obtained from google sign in use this
if (mGoogleApiClient.hasConnectedApi(Plus.API)) {
Person person = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);
if (person != null) {
//Take the required action
Log.i(TAG, "Display Name: " + person.getDisplayName());
Log.i(TAG, "Gender: " + person.getGender());
Log.i(TAG, "About Me: " + person.getAboutMe());
Log.i(TAG, "Birthday: " + person.getBirthday());
Log.i(TAG, "Current Location: " + person.getCurrentLocation());
Log.i(TAG, "Language: " + person.getLanguage());
} else {
Log.e(TAG, "Error!");
}

Android Broadcast Issue - 60 Second Delay in Receiving Message

We have a process that broadcasts a message using the following code. This message is received and processed by a BroadcastReceiver (given below). We have a curious problem on some Android tablets and phones. The message that is broadcast is not received by the receiver for exactly 60 seconds. It is as if there is something that prevents the message from being broadcasted/delivered for 60 seconds. We have triple checked the code and there is no artificial delay being introduced in the code. Has anyone else seen this issue? We will appreciate any insight on the issue.
We are using Xamarin.Android 4.1.0 and have seen the issue on Samsung Note 10.1 and Note 3. We have not seen it on Nexus 7 or Sony tablets.
public static void BroadcastResult(string SN, string result, string errorMessage, string commandType = "DataBatch")
{
try
{
Android.Content.Intent broadcastIntent = new Android.Content.Intent("com.GoServicePro.ProcessorResults");
broadcastIntent.PutExtra("Type", commandType);
broadcastIntent.PutExtra("SN", SN.ToString());
broadcastIntent.PutExtra("RESULT", result);
broadcastIntent.PutExtra("EXCEPTION", errorMessage);
if (Global.gCustomizerCol != null)
{
if (Global.gCustomizerCol.Count > 0)
{
}
}
if (Global.CurrentContext == null)
Android.App.Application.Context.SendBroadcast(broadcastIntent);
else
Global.CurrentContext.SendBroadcast(broadcastIntent);
Logging.LogToProcessorLog("Broadcasted results: <" + commandType + ":" + SN + ":" + result + ":" + errorMessage + ">");
}
catch (Exception ex)
{
Logging.LogToProcessorLog("EXCEPTION: " + ex.Message + "\n" + ex.StackTrace);
}
}
[BroadcastReceiver(Enabled = true, Label = "GoServicePro Receiver")]
[IntentFilter(new string[] { "com.GoServicePro.ProcessorResults" })]
public class ProcessorReceiver : BroadcastReceiver
{
/// <summary>
/// Handler for processing completed message brodcasted by GoServiceProProcessorService.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
string commandType = intent.GetStringExtra("Type");
try
{
string sn = intent.GetStringExtra("SN");
string result = intent.GetStringExtra("RESULT");
string error = intent.GetStringExtra("EXCEPTION");
switch (commandType)
{
case "DataBatch":
if (result == "SUCCESS")
{
Global.gintGSASeqNumIn += 1;
Database.UpdateBootDataSeqInfo();
Communication.QueueAck(Global.gintGSASeqNumIn);
Logging.LogMsg("Batch# " + sn + " was processed successfully.");
if (Global.SendRefresh != null)
Global.SendRefresh.Invoke(true, "ACTIVE_TAB", true, "GENERAL", 0, true);
Logging.LogMsg("Refresh sent to Main and WO forms", 2);
}
else if (result == "ABORT_FOR_BEGIN_REFRESH")
Logging.LogMsg("Batch# " + sn + " was aborted by SECONDARY process because it had BEGIN_FULL_REFRESH command. It will be processed by PRIMARY process");
else
{
Logging.LogMsg("Batch with serial# " + sn + " was NOT saved successfully. Error was:\n" + error);
Logging.LogMsg("Will retry batch with serial# " + sn);
}
break;
.
.
.
.

Categories

Resources