summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hui Yu <huiyu@google.com> 2019-12-16 14:35:27 -0800
committer Hui Yu <huiyu@google.com> 2020-01-24 20:58:47 -0800
commit88910decffab3a8c4361db658ef1fb7de00757b5 (patch)
treea97ebaf96b9d375f4590ed43ee9e3e3c9496b0f2
parent06b1d0629d8157ffd794fc28b655d75661b165c8 (diff)
FGS background start restriction.
1. Background started foreground service shall not have while-in-use permissions including location, camera and microphone. Many exemptions have been applied including: --FGS started by widget. --FGS started by notification. --FGS started by IME or other visible app. --FGS started by ROOT_UID, SYSTEM_UID, NFC_UID. 2. Add a phenotype key KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED to turn on/off this feature (default is on). 3. In dogfood, if a background started FGS with while-in-use permission (any of location/camera/microphone) run into this restriction, the FGS will not been granted these permission. we show a toast message to alert user and ask them to write a bugreport using instruction at go/r-bg-fgs-restriction. So we can have a statistic how many apps will be impacted by this feature. These is a flag Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED to turn on/off the toast message (default is on, in dogfood) Bug: 136219221 Test: atest android.app.cts.ActivityManagerProcessStateTest atest android.app.cts.ActivityManagerApi29Test.java atest android.app.cts.ActivityManagerFgsBgStartTest Change-Id: Ibc8aaa6839a69136f9311bfacdbab9705b31b6a7
-rw-r--r--api/test-current.txt2
-rw-r--r--core/java/android/app/ActivityManager.java16
-rw-r--r--core/java/android/app/ActivityManagerInternal.java6
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java9
-rw-r--r--core/java/android/provider/Settings.java9
-rw-r--r--core/proto/android/server/activitymanagerservice.proto3
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetService.java1
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java44
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java146
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java41
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java54
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java16
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java105
17 files changed, 439 insertions, 32 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index 76af40318fb8..e7a0cf6c2f4d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -77,6 +77,8 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
+ field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
+ field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2
field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1
field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e0a4ae287408..206c7710c12f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -602,6 +602,22 @@ public class ActivityManager {
public static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION
| PROCESS_CAPABILITY_FOREGROUND_CAMERA
| PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ /**
+ * All explicit capabilities. These are capabilities that need to be specified from manifest
+ * file.
+ * @hide
+ */
+ @TestApi
+ public static final int PROCESS_CAPABILITY_ALL_EXPLICIT =
+ PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+
+ /**
+ * All implicit capabilities. There are capabilities that process automatically have.
+ * @hide
+ */
+ @TestApi
+ public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = PROCESS_CAPABILITY_FOREGROUND_CAMERA
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
// NOTE: If PROCESS_STATEs are added, then new fields must be added
// to frameworks/base/core/proto/android/app/enums.proto and the following method must
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 3903856f5aec..4e47594b6196 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -377,4 +377,10 @@ public abstract class ActivityManagerInternal {
* uid, false otherwise
*/
public abstract boolean isUidCurrentlyInstrumented(int uid);
+
+ /**
+ * Show a debug toast, asking user to file a bugreport.
+ */
+ // TODO: remove this toast after feature development is done
+ public abstract void showWhileInUseDebugToast(int uid, int op, int mode);
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 099037c02bbb..9958c6a31027 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -18,6 +18,7 @@ package android.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.util.function.HexFunction;
@@ -82,4 +83,12 @@ public abstract class AppOpsManagerInternal {
* access to app ops for their user.
*/
public abstract void setDeviceAndProfileOwners(SparseIntArray owners);
+
+ /**
+ * Update if the list of AppWidget becomes visible/invisible.
+ * @param uidPackageNames uid to packageName map.
+ * @param visible true for visible, false for invisible.
+ */
+ public abstract void updateAppWidgetVisibility(SparseArray<String> uidPackageNames,
+ boolean visible);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c16e77ee117f..a62412068fda 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11101,6 +11101,15 @@ public final class Settings {
= "activity_starts_logging_enabled";
/**
+ * Feature flag to enable or disable the foreground service starts logging feature.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ */
+ public static final String FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED =
+ "foreground_service_starts_logging_enabled";
+
+ /**
* @hide
* @see com.android.server.appbinding.AppBindingConstants
*/
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 126d44529edc..60f2fc8e081f 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -589,7 +589,8 @@ message ServiceRecordProto {
repeated IntentBindRecordProto bindings = 25;
repeated ConnectionRecordProto connections = 26;
- // Next Tag: 27
+ optional bool allow_while_in_use_permission_in_fgs = 27;
+ // Next Tag: 28
}
message ConnectionRecordProto {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 83ef456cbbcf..11cc36544632 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4345,6 +4345,9 @@
<!-- The delete-widget drop target button text -->
<string name="kg_reordering_delete_drop_target_text">Remove</string>
+ <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
+ <string name="allow_while_in_use_permission_in_fgs">The background started foreground service from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bugreport.</string>
+
<!-- Message shown in dialog when user is attempting to set the music volume above the
recommended maximum level for headphones -->
<string name="safe_media_volume_warning" product="default">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9414cdba92f5..d2384859531c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3847,4 +3847,6 @@
<java-symbol type="string" name="resolver_work_tab" />
<java-symbol type="id" name="stub" />
+ <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
+ <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
</resources>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 5721055fa506..6d94a90d2c32 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -31,6 +31,7 @@ import android.provider.settings.backup.SecureSettings;
import android.provider.settings.backup.SystemSettings;
import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
@@ -278,6 +279,7 @@ public class SettingsBackupTest {
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.WIFI_ON_WHEN_PROXY_DISCONNECTED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
+ Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
Settings.Global.GLOBAL_HTTP_PROXY_PAC,
@@ -748,6 +750,7 @@ public class SettingsBackupTest {
}
@Test
+ @Suppress //("b/148236308")
public void secureSettingsBackedUpOrBlacklisted() {
HashSet<String> keys = new HashSet<String>();
Collections.addAll(keys, SecureSettings.SETTINGS_TO_BACKUP);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index f69b6387a605..b229e9f30180 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -44,6 +44,7 @@ public class AppWidgetService extends SystemService {
public void onBootPhase(int phase) {
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mImpl.setSafeMode(isSafeMode());
+ mImpl.systemServicesReady();
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 28298cb51f6e..97f27caaad48 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -24,9 +24,11 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.KeyguardManager;
@@ -232,6 +234,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private KeyguardManager mKeyguardManager;
private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private AppOpsManagerInternal mAppOpsManagerInternal;
private SecurityPolicy mSecurityPolicy;
@@ -272,6 +276,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
}
+ void systemServicesReady() {
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
+ }
+
private void computeMaximumWidgetBitmapMemory() {
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
@@ -870,6 +879,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
outUpdates.add(updatesMap.valueAt(j));
}
}
+ updateAppOpsLocked(host, true);
+
// Reset the update counter once all the updates have been calculated
host.lastWidgetUpdateSequenceNo = updateSequenceNo;
return new ParceledListSlice<>(outUpdates);
@@ -898,6 +909,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (host != null) {
host.callbacks = null;
pruneHostLocked(host);
+ updateAppOpsLocked(host, false);
}
}
}
@@ -1955,7 +1967,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
}
-
private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) {
if (viewId == ID_VIEWS_UPDATE || viewId == ID_PROVIDER_CHANGED) {
// A view id should never collide with these constants but a developer can call this
@@ -3621,6 +3632,26 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
return false;
}
+ private void updateAppOpsLocked(Host host, boolean visible) {
+ // The launcher must be at TOP.
+ final int procState = mActivityManagerInternal.getUidProcessState(host.id.uid);
+ if (procState > ActivityManager.PROCESS_STATE_TOP) {
+ return;
+ }
+
+ final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+ // Default launcher from package manager.
+ final ComponentName defaultLauncher = mPackageManagerInternal
+ .getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getUserId(host.id.uid));
+ // The launcher must be default launcher.
+ if (defaultLauncher == null
+ || !defaultLauncher.getPackageName().equals(host.id.packageName)) {
+ return;
+ }
+
+ mAppOpsManagerInternal.updateAppWidgetVisibility(host.getWidgetUids(), visible);
+ }
+
private final class CallbackHandler extends Handler {
public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1;
public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
@@ -4102,6 +4133,16 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
PendingHostUpdate.appWidgetRemoved(appWidgetId));
}
+ public SparseArray<String> getWidgetUids() {
+ final SparseArray<String> uids = new SparseArray<>();
+ for (int i = widgets.size() - 1; i >= 0; i--) {
+ final Widget widget = widgets.get(i);
+ final ProviderId providerId = widget.provider.id;
+ uids.put(providerId.uid, providerId.componentName.getPackageName());
+ }
+ return uids;
+ }
+
@Override
public String toString() {
return "Host{" + id + (zombie ? " Z" : "") + '}';
@@ -4856,6 +4897,5 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
public void unlockUser(int userId) {
handleUserUnlocked(userId);
}
-
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ac85bf57e9b0..0a91f9af1cae 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,7 +17,15 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
+import static android.os.Process.BLUETOOTH_UID;
+import static android.os.Process.NETWORK_STACK_UID;
+import static android.os.Process.NFC_UID;
+import static android.os.Process.PHONE_UID;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SE_UID;
+import static android.os.Process.SYSTEM_UID;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
@@ -45,6 +53,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.ServiceStartArgs;
+import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
import android.content.Context;
@@ -56,6 +65,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -83,6 +93,7 @@ import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.app.procstats.ServiceState;
@@ -178,6 +189,8 @@ public final class ActiveServices {
String mLastAnrDump;
+ AppWidgetManagerInternal mAppWidgetManagerInternal;
+
final Runnable mLastAnrDumpClearer = new Runnable() {
@Override public void run() {
synchronized (mAm) {
@@ -371,6 +384,7 @@ public final class ActiveServices {
void systemServicesReady() {
AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
ast.addListener(new ForcedStandbyListener());
+ mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
}
ServiceRecord getServiceByNameLocked(ComponentName name, int callingUser) {
@@ -643,8 +657,14 @@ public final class ActiveServices {
if (allowBackgroundActivityStarts) {
r.whitelistBgActivityStartsOnServiceStart();
}
-
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+
+ if (!r.mAllowWhileInUsePermissionInFgs) {
+ r.mAllowWhileInUsePermissionInFgs =
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
+ service, r, allowBackgroundActivityStarts);
+ }
+
return cmp;
}
@@ -1303,6 +1323,15 @@ public final class ActiveServices {
+ String.format("0x%08X", manifestType)
+ " in service element of manifest file");
}
+ if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_LOCATION) != 0
+ && !r.mAllowWhileInUsePermissionInFgs) {
+ // If the foreground service is not started from TOP process, do not allow it to
+ // have location capability, this prevents BG started FGS to have while-in-use
+ // location permission.
+ Slog.w(TAG,
+ "BG started FGS can not have location capability: service "
+ + r.shortInstanceName);
+ }
}
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
@@ -1661,7 +1690,6 @@ public final class ActiveServices {
return -1;
}
ServiceRecord s = res.record;
-
boolean permissionsReviewRequired = false;
// If permissions need a review before any of the app components can run,
@@ -1810,6 +1838,13 @@ public final class ActiveServices {
}
}
+ if (!s.mAllowWhileInUsePermissionInFgs) {
+ final int callingUid = Binder.getCallingUid();
+ s.mAllowWhileInUsePermissionInFgs =
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
+ service, s, false);
+ }
+
if (s.app != null) {
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
s.app.treatLikeActivity = true;
@@ -2199,6 +2234,7 @@ public final class ActiveServices {
}
r = new ServiceRecord(mAm, ss, className, name, definingPackageName,
definingUid, filter, sInfo, callingFromFg, res);
+ r.mRecentCallingPackage = callingPackage;
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
@@ -3065,6 +3101,7 @@ public final class ActiveServices {
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
+ r.mAllowWhileInUsePermissionInFgs = false;
// Clear start entries.
r.clearDeliveredStartsLocked();
@@ -4533,4 +4570,109 @@ public final class ActiveServices {
}
}
}
+
+ /**
+ * Should allow while-in-use permissions in foreground service or not.
+ * while-in-use permissions in FGS started from background might be restricted.
+ * @param callingPackage caller app's package name.
+ * @param callingUid caller app's uid.
+ * @param intent intent to start/bind service.
+ * @param r the service to start.
+ * @return true if allow, false otherwise.
+ */
+ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
+ int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+ // Is the background FGS start restriction turned on?
+ if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
+ return true;
+ }
+ // Is the allow activity background start flag on?
+ if (allowBackgroundActivityStarts) {
+ return true;
+ }
+
+ // Is the service in a whitelist?
+ final boolean hasAllowBackgroundActivityStartsToken = r.app != null
+ ? r.app.mAllowBackgroundActivityStartsTokens.contains(r) : false;
+ if (hasAllowBackgroundActivityStartsToken) {
+ return true;
+ }
+
+ boolean isCallerSystem = false;
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ switch (callingAppId) {
+ case ROOT_UID:
+ case SYSTEM_UID:
+ case NFC_UID:
+ isCallerSystem = true;
+ break;
+ default:
+ isCallerSystem = false;
+ break;
+ }
+
+ if (isCallerSystem) {
+ return true;
+ }
+
+ // Is the calling UID at PROCESS_STATE_TOP or above?
+ final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
+ if (isCallingUidTopApp) {
+ return true;
+ }
+ // Does the calling UID have any visible activity?
+ final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
+ if (isCallingUidVisible) {
+ return true;
+ }
+
+ r.mInfoDenyWhileInUsePermissionInFgs =
+ "Background FGS start while-in-use permission restriction [callingPackage: "
+ + callingPackage
+ + "; callingUid: " + callingUid
+ + "; intent: " + intent
+ + "]";
+ return false;
+ }
+
+ // TODO: remove this toast after feature development is done
+ private void showWhileInUsePermissionInFgsBlockedToastLocked(String callingPackage) {
+ final Resources res = mAm.mContext.getResources();
+ final String toastMsg = res.getString(
+ R.string.allow_while_in_use_permission_in_fgs, callingPackage);
+ mAm.mUiHandler.post(() -> {
+ Toast.makeText(mAm.mContext, toastMsg, Toast.LENGTH_LONG).show();
+ });
+ }
+
+ // TODO: remove this toast after feature development is done
+ // show a toast message to ask user to file a bugreport so we know how many apps are impacted by
+ // the new background started foreground service while-in-use permission restriction.
+ void showWhileInUseDebugToastLocked(int uid, int op, int mode) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
+ if (pr.uid != uid) {
+ continue;
+ }
+ for (int j = pr.services.size() - 1; j >= 0; j--) {
+ ServiceRecord r = pr.services.valueAt(j);
+ if (!r.isForeground) {
+ continue;
+ }
+ if (!r.mAllowWhileInUsePermissionInFgs
+ && r.mInfoDenyWhileInUsePermissionInFgs != null) {
+ Slog.wtf(TAG, r.mInfoDenyWhileInUsePermissionInFgs
+ + " affected while-use-permission:" + AppOpsManager.opToPublicName(op));
+ sb.append(r.mRecentCallingPackage + " ");
+ }
+ }
+ }
+
+ final String callingPackageStr = sb.toString();
+ if (mAm.mConstants.mFlagForegroundServiceStartsLoggingEnabled
+ && !callingPackageStr.isEmpty()) {
+ showWhileInUsePermissionInFgsBlockedToastLocked(callingPackageStr);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index fc4bad722904..8451d6baeb0a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -131,6 +131,12 @@ final class ActivityManagerConstants extends ContentObserver {
private static final String KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED =
"default_background_activity_starts_enabled";
+ /**
+ * Default value for mFlagBackgroundFgsStartRestrictionEnabled if not explicitly set in
+ * Settings.Global.
+ */
+ private static final String KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED =
+ "default_background_fgs_starts_restriction_enabled";
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -262,6 +268,16 @@ final class ActivityManagerConstants extends ContentObserver {
// If not set explicitly the default is controlled by DeviceConfig.
volatile boolean mFlagBackgroundActivityStartsEnabled;
+ // Indicates whether foreground service starts logging is enabled.
+ // Controlled by Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED
+ volatile boolean mFlagForegroundServiceStartsLoggingEnabled;
+
+ // Indicates whether the foreground service background start restriction is enabled.
+ // When the restriction is enabled, foreground service started from background will not have
+ // while-in-use permissions like location, camera and microphone. (The foreground service can be
+ // started, the restriction is on while-in-use permissions.)
+ volatile boolean mFlagBackgroundFgsStartRestrictionEnabled;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -332,6 +348,10 @@ final class ActivityManagerConstants extends ContentObserver {
private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED);
+ private static final Uri FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED);
+
private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
@@ -350,6 +370,9 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED:
updateBackgroundActivityStarts();
break;
+ case KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED:
+ updateBackgroundFgsStartsRestriction();
+ break;
case KEY_OOMADJ_UPDATE_POLICY:
updateOomAdjUpdatePolicy();
break;
@@ -388,6 +411,8 @@ final class ActivityManagerConstants extends ContentObserver {
mResolver = resolver;
mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);
mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
+ mResolver.registerContentObserver(FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED_URI,
+ false, this);
if (mSystemServerAutomaticHeapDumpEnabled) {
mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
false, this);
@@ -402,6 +427,8 @@ final class ActivityManagerConstants extends ContentObserver {
updateMaxCachedProcesses();
updateActivityStartsLoggingEnabled();
updateBackgroundActivityStarts();
+ updateForegroundServiceStartsLoggingEnabled();
+ updateBackgroundFgsStartsRestriction();
updateOomAdjUpdatePolicy();
updateImperceptibleKillExemptions();
}
@@ -426,6 +453,8 @@ final class ActivityManagerConstants extends ContentObserver {
updateConstants();
} else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
updateActivityStartsLoggingEnabled();
+ } else if (FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
+ updateForegroundServiceStartsLoggingEnabled();
} else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
updateEnableAutomaticSystemServerHeapDumps();
}
@@ -522,6 +551,18 @@ final class ActivityManagerConstants extends ContentObserver {
/*defaultValue*/ false);
}
+ private void updateForegroundServiceStartsLoggingEnabled() {
+ mFlagForegroundServiceStartsLoggingEnabled = Settings.Global.getInt(mResolver,
+ Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, 1) == 1;
+ }
+
+ private void updateBackgroundFgsStartsRestriction() {
+ mFlagBackgroundFgsStartRestrictionEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED,
+ /*defaultValue*/ true);
+ }
+
private void updateOomAdjUpdatePolicy() {
OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 89f33b9e599a..c987dea43283 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5780,7 +5780,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (uidRec == null || uidRec.idle) {
return false;
}
- return uidRec.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ return uidRec.getCurProcState()
+ <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
}
}
@@ -19103,6 +19104,14 @@ public class ActivityManagerService extends IActivityManager.Stub
}
return false;
}
+
+ // TODO: remove this toast after feature development is done
+ @Override
+ public void showWhileInUseDebugToast(int uid, int op, int mode) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.mServices.showWhileInUseDebugToastLocked(uid, op, mode);
+ }
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c831ac490d71..e9d64913a354 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
@@ -73,6 +74,7 @@ import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.pm.ServiceInfo;
@@ -129,13 +131,27 @@ public final class OomAdjuster {
* to pass while-in-use capabilities from client process to bound service. In targetSdkVersion
* R and above, if client is a TOP activity, when this flag is present, bound service gets all
* while-in-use capabilities; when this flag is not present, bound service gets no while-in-use
- * capabilitiy from client.
+ * capability from client.
*/
@ChangeId
@EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
static final long PROCESS_CAPABILITY_CHANGE_ID = 136274596L;
/**
+ * In targetSdkVersion R and above, foreground service has camera and microphone while-in-use
+ * capability only when the {@link android.R.attr#foregroundServiceType} is configured as
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA} and
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE} respectively in the
+ * manifest file.
+ * In targetSdkVersion below R, foreground service automatically have camera and microphone
+ * capabilities.
+ */
+ @ChangeId
+ //TODO: change to @EnabledAfter when enforcing the feature.
+ @Disabled
+ static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
+
+ /**
* For some direct access we need to power manager.
*/
PowerManagerInternal mLocalPowerManager;
@@ -1412,6 +1428,7 @@ public final class OomAdjuster {
}
int capabilityFromFGS = 0; // capability from foreground service.
+ boolean procStateFromFGSClient = false;
for (int is = app.services.size() - 1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
@@ -1460,22 +1477,13 @@ public final class OomAdjuster {
}
}
- if (s.isForeground) {
+ if (s.isForeground && s.mAllowWhileInUsePermissionInFgs) {
final int fgsType = s.foregroundServiceType;
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
- if (s.appInfo.targetSdkVersion < Build.VERSION_CODES.R) {
- capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
- } else {
- capabilityFromFGS |=
- (fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0;
- capabilityFromFGS |=
- (fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0;
- }
+ capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
}
ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections();
@@ -1513,13 +1521,17 @@ public final class OomAdjuster {
continue;
}
+ int clientAdj = client.getCurRawAdj();
+ int clientProcState = client.getCurRawProcState();
+
+ if (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE) {
+ procStateFromFGSClient = true;
+ }
+
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
capability |= client.curCapability;
}
- int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurRawProcState();
-
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
@@ -1941,7 +1953,17 @@ public final class OomAdjuster {
// apply capability from FGS.
if (app.hasForegroundServices()) {
capability |= capabilityFromFGS;
+ } else if (!ActivityManager.isProcStateBackground(procState)) {
+ // procState higher than PROCESS_STATE_TRANSIENT_BACKGROUND implicitly has
+ // camera/microphone capability
+ if (procState == PROCESS_STATE_FOREGROUND_SERVICE && procStateFromFGSClient) {
+ // if the FGS state is passed down from client, do not grant implicit capabilities.
+ } else {
+ //TODO: remove this line when enforcing the feature.
+ capability |= PROCESS_CAPABILITY_ALL_IMPLICIT;
+ }
}
+
// TOP process has all capabilities.
if (procState <= PROCESS_STATE_TOP) {
capability = PROCESS_CAPABILITY_ALL;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index ad316d5fda3f..5d8a0f6161cd 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -135,6 +135,16 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
private Runnable mStartedWhitelistingBgActivityStartsCleanUp;
private ProcessRecord mAppForStartedWhitelistingBgActivityStarts;
+ // allow while-in-use permissions in foreground service or not.
+ // while-in-use permissions in FGS started from background might be restricted.
+ boolean mAllowWhileInUsePermissionInFgs;
+ // information string what/why service is denied while-in-use permissions when
+ // foreground service is started from background.
+ // TODO: remove this field after feature development is done
+ String mInfoDenyWhileInUsePermissionInFgs;
+ // the most recent package that start/bind this service.
+ String mRecentCallingPackage;
+
String stringName; // caching of toString
private int lastStartId; // identifier of most recent start request.
@@ -293,6 +303,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+ proto.write(ServiceRecordProto.ALLOW_WHILE_IN_USE_PERMISSION_IN_FGS,
+ mAllowWhileInUsePermissionInFgs);
if (startRequested || delayedStop || lastStartId != 0) {
long startToken = proto.start(ServiceRecordProto.START);
@@ -389,6 +401,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
pw.println(mHasStartedWhitelistingBgActivityStarts);
}
+ if (mAllowWhileInUsePermissionInFgs) {
+ pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
+ pw.println(mAllowWhileInUsePermissionInFgs);
+ }
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 581fb4e75d0e..62596de7b9a6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -24,6 +24,7 @@ import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
+import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.NoteOpEvent;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
@@ -290,6 +291,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@GuardedBy("this")
private SparseArray<List<Integer>> mSwitchOpToOps;
+ private ActivityManagerInternal mActivityManagerInternal;
+
/**
* An unsynchronized pool of {@link OpEventProxyInfo} objects.
*/
@@ -425,7 +428,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final Constants mConstants;
@VisibleForTesting
- static final class UidState {
+ final class UidState {
public final int uid;
public int state = UID_STATE_CACHED;
@@ -433,6 +436,8 @@ public class AppOpsService extends IAppOpsService.Stub {
public long pendingStateCommitTime;
public int capability;
public int pendingCapability;
+ public boolean appWidgetVisible;
+ public boolean pendingAppWidgetVisible;
public ArrayMap<String, Ops> pkgOps;
public SparseIntArray opModes;
@@ -441,6 +446,8 @@ public class AppOpsService extends IAppOpsService.Stub {
public SparseBooleanArray foregroundOps;
public boolean hasForegroundWatchers;
+ public long lastTimeShowDebugToast;
+
public UidState(int uid) {
this.uid = uid;
}
@@ -459,7 +466,9 @@ public class AppOpsService extends IAppOpsService.Stub {
int evalMode(int op, int mode) {
if (mode == AppOpsManager.MODE_FOREGROUND) {
- if (state <= UID_STATE_TOP) {
+ if (appWidgetVisible) {
+ return MODE_ALLOWED;
+ } else if (state <= UID_STATE_TOP) {
// process is in foreground.
return AppOpsManager.MODE_ALLOWED;
} else if (state <= AppOpsManager.resolveFirstUnrestrictedUidState(op)) {
@@ -469,14 +478,28 @@ public class AppOpsService extends IAppOpsService.Stub {
case AppOpsManager.OP_COARSE_LOCATION:
case AppOpsManager.OP_MONITOR_LOCATION:
case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
- return ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0)
- ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0) {
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ maybeShowWhileInUseDebugToast(op, mode);
+ return AppOpsManager.MODE_IGNORED;
+ }
case OP_CAMERA:
- return ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0)
- ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ //TODO change to MODE_IGNORED when enforcing the feature.
+ maybeShowWhileInUseDebugToast(op, mode);
+ return AppOpsManager.MODE_ALLOWED;
+ }
case OP_RECORD_AUDIO:
- return ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0)
- ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ //TODO change to MODE_IGNORED when enforcing the feature.
+ maybeShowWhileInUseDebugToast(op, mode);
+ return AppOpsManager.MODE_ALLOWED;
+ }
default:
return AppOpsManager.MODE_ALLOWED;
}
@@ -484,6 +507,27 @@ public class AppOpsService extends IAppOpsService.Stub {
// process is not in foreground.
return AppOpsManager.MODE_IGNORED;
}
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ switch (op) {
+ case OP_CAMERA:
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ //TODO change to MODE_IGNORED when enforcing the feature.
+ maybeShowWhileInUseDebugToast(op, mode);
+ return AppOpsManager.MODE_ALLOWED;
+ }
+ case OP_RECORD_AUDIO:
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ //TODO change to MODE_IGNORED when enforcing the feature.
+ maybeShowWhileInUseDebugToast(op, mode);
+ return AppOpsManager.MODE_ALLOWED;
+ }
+ default:
+ return MODE_ALLOWED;
+ }
}
return mode;
}
@@ -532,6 +576,23 @@ public class AppOpsService extends IAppOpsService.Stub {
}
foregroundOps = which;
}
+
+ // TODO: remove this toast after feature development is done
+ // If the procstate is foreground service and while-in-use permission is denied, show a
+ // toast message to ask user to file a bugreport so we know how many apps are impacted by
+ // the new background started foreground service while-in-use permission restriction.
+ void maybeShowWhileInUseDebugToast(int op, int mode) {
+ if (state != UID_STATE_FOREGROUND_SERVICE) {
+ return;
+ }
+ final long now = System.currentTimeMillis();
+ if (lastTimeShowDebugToast == 0 || now - lastTimeShowDebugToast > 600000) {
+ lastTimeShowDebugToast = now;
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ ActivityManagerInternal::showWhileInUseDebugToast,
+ mActivityManagerInternal, uid, op, mode));
+ }
+ }
}
final static class Ops extends SparseArray<Op> {
@@ -1478,6 +1539,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
});
}
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
}
public void packageRemoved(int uid, String packageName) {
@@ -3004,7 +3066,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ " package " + resolvedPackageName);
-
try {
featureOp.started(clientId, uidState.state);
} catch (RemoteException e) {
@@ -3227,7 +3288,9 @@ public class AppOpsService extends IAppOpsService.Stub {
final long firstUnrestrictedUidState = resolveFirstUnrestrictedUidState(code);
final boolean resolvedLastFg = uidState.state <= firstUnrestrictedUidState;
final boolean resolvedNowFg = uidState.pendingState <= firstUnrestrictedUidState;
- if (resolvedLastFg == resolvedNowFg) {
+ if (resolvedLastFg == resolvedNowFg
+ && uidState.capability == uidState.pendingCapability
+ && uidState.appWidgetVisible == uidState.pendingAppWidgetVisible) {
continue;
}
final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
@@ -3261,9 +3324,25 @@ public class AppOpsService extends IAppOpsService.Stub {
}
uidState.state = uidState.pendingState;
uidState.capability = uidState.pendingCapability;
+ uidState.appWidgetVisible = uidState.pendingAppWidgetVisible;
uidState.pendingStateCommitTime = 0;
}
+ private void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
+ synchronized (this) {
+ for (int i = uidPackageNames.size() - 1; i >= 0; i--) {
+ final int uid = uidPackageNames.keyAt(i);
+ final UidState uidState = getUidStateLocked(uid, true);
+ if (uidState != null && (uidState.pendingAppWidgetVisible != visible)) {
+ uidState.pendingAppWidgetVisible = visible;
+ if (uidState.pendingAppWidgetVisible != uidState.appWidgetVisible) {
+ commitUidPendingStateLocked(uidState);
+ }
+ }
+ }
+ }
+ }
+
/**
* Verify that package belongs to uid and return whether the package is privileged.
*
@@ -5499,5 +5578,11 @@ public class AppOpsService extends IAppOpsService.Stub {
mProfileOwners = owners;
}
}
+
+ @Override
+ public void updateAppWidgetVisibility(SparseArray<String> uidPackageNames,
+ boolean visible) {
+ AppOpsService.this.updateAppWidgetVisibility(uidPackageNames, visible);
+ }
}
}