From 583ad82ddcf1983a68b24da2faf9e1d2d0241ed5 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Thu, 15 Jul 2021 00:56:19 +0000 Subject: [PATCH 001/208] Revert "wifidisplay: restrict broadcast by the proper permission" This reverts commit cff8340c84ad1d6c0b3deae6a42f781e7db64082. Reason for revert: Remove from July 2021 Android Security Bulletin due to break existing applications. Bug: 176541017 Change-Id: Iacef744056630e85fb43d838a72abfae331cbaf4 Test: install WFD application and check whether it works normally. (cherry picked from commit 955b64a6e71e21993c254bc4a1a12b507f5fd65d) --- core/java/android/hardware/display/DisplayManager.java | 3 --- .../com/android/server/display/WifiDisplayAdapter.java | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index cfacd5d97bcb..0fa4ca8a574b 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -61,9 +61,6 @@ public final class DisplayManager { * {@link #EXTRA_WIFI_DISPLAY_STATUS} extra. *

* This broadcast is only sent to registered receivers and can only be sent by the system. - *

- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is required to - * receive this broadcast. *

* @hide */ diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index 551df49b550f..57323170b327 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -91,10 +91,6 @@ final class WifiDisplayAdapter extends DisplayAdapter { private boolean mPendingStatusChangeBroadcast; - private static final String[] RECEIVER_PERMISSIONS_FOR_BROADCAST = { - android.Manifest.permission.ACCESS_FINE_LOCATION, - }; - // Called with SyncRoot lock held. public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, @@ -436,8 +432,7 @@ private void handleSendStatusChangeBroadcast() { } // Send protected broadcast about wifi display status to registered receivers. - getContext().createContextAsUser(UserHandle.ALL, 0) - .sendBroadcastWithMultiplePermissions(intent, RECEIVER_PERMISSIONS_FOR_BROADCAST); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { From 559b87f7ff1988e4b28411e89fbab26112cd17b3 Mon Sep 17 00:00:00 2001 From: Hui Yu Date: Wed, 25 Aug 2021 22:06:59 +0000 Subject: [PATCH 002/208] Revert "BG-FGS-start while-in-use permission restriction improve..." Revert "Add test cases for background startForeground() improvement." Revert submission 15081873-cherrypick-security_backport_183147114-an8dvy98fv Reason for revert: https://b.corp.google.com/issues/197066403#comment15 After the app calls Service.startForeground() to put the app into foreground service mode, the service has mAllowWhileInUsePermissionInFgs set, if it is true, the app/service can access location/camera/micophone. Some apps may call Service.startForeground() again (for any reason, could be a app's unintentional redundant call, could be that app want to update the notification of the foreground service), but at this moment the app has went into background mode, although the foreground service is still running. The second or later Service.startForeground() call may set mAllowWhileInUsePermissionInFgs to false and the app may lose location/camera/micophone access. This is incorrect because the foreground service is still running and we expect location/camera/micophone access to continue. The Samsung Voice Recorder app has run into this situation. Reverted Changes: I0aca484e5:BG-FGS-start while-in-use permission restriction i... I4988dbba1:Add test cases for background startForeground() im... Bug: 183147114 Bug: 197066403 Change-Id: Iad32e4391fa15bc252e50f9b858fe2e5225edb19 Merged-In: Idc88f274c7a323d175d65bb47eca041772ae9bb7 (cherry picked from commit c83a949722d0b879fb7ac73f3740e45d08b8efa6) --- .../com/android/server/am/ActiveServices.java | 107 ++++-------------- .../server/am/ActivityManagerConstants.java | 20 ---- .../com/android/server/am/ServiceRecord.java | 6 - 3 files changed, 21 insertions(+), 112 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 1cd7115f8edc..e4dd8756fe62 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -734,8 +734,11 @@ ComponentName startServiceLocked(IApplicationThread caller, Intent service, Stri } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r, - allowBackgroundActivityStarts); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = + shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, + callingUid, service, r, allowBackgroundActivityStarts); + } return cmp; } @@ -1408,6 +1411,14 @@ private void setServiceForegroundInnerLocked(final ServiceRecord r, int id, + String.format("0x%08X", manifestType) + " in service element of manifest file"); } + // If the foreground service is not started from TOP process, do not allow it to + // have while-in-use location/camera/microphone access. + if (!r.mAllowWhileInUsePermissionInFgs) { + Slog.w(TAG, + "Foreground service started from background can not have " + + "location/camera/microphone access: service " + + r.shortInstanceName); + } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; @@ -1455,57 +1466,6 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { ignoreForeground = true; } - if (!ignoreForeground) { - if (r.mStartForegroundCount == 0) { - /* - If the service was started with startService(), not - startForegroundService(), and if startForeground() isn't called within - mFgsStartForegroundTimeoutMs, then we check the state of the app - (who owns the service, which is the app that called startForeground()) - again. If the app is in the foreground, or in any other cases where - FGS-starts are allowed, then we still allow the FGS to be started. - Otherwise, startForeground() would fail. - - If the service was started with startForegroundService(), then the service - must call startForeground() within a timeout anyway, so we don't need this - check. - */ - if (!r.fgRequired) { - final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime; - if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { - resetFgsRestrictionLocked(r); - setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, - r.appInfo.uid, r, false); - EventLog.writeEvent(0x534e4554, "183147114", - r.appInfo.uid, - "call setFgsRestrictionLocked again due to " - + "startForegroundTimeout"); - } - } - } else if (r.mStartForegroundCount >= 1) { - // The second or later time startForeground() is called after service is - // started. Check for app state again. - final long delayMs = SystemClock.elapsedRealtime() - - r.mLastSetFgsRestrictionTime; - if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { - resetFgsRestrictionLocked(r); - setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, - r.appInfo.uid, r, false); - EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid, - "call setFgsRestrictionLocked for " - + (r.mStartForegroundCount + 1) + "th startForeground"); - } - } - // If the foreground service is not started from TOP process, do not allow it to - // have while-in-use location/camera/microphone access. - if (!r.mAllowWhileInUsePermissionInFgs) { - Slog.w(TAG, - "Foreground service started from background can not have " - + "location/camera/microphone access: service " - + r.shortInstanceName); - } - } - // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -1541,7 +1501,6 @@ must call startForeground() within a timeout anyway, so we don't need this active.mNumActive++; } r.isForeground = true; - r.mStartForegroundCount++; if (!stopProcStatsOp) { ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1600,7 +1559,6 @@ must call startForeground() within a timeout anyway, so we don't need this decActiveForegroundAppLocked(smap, r); } r.isForeground = false; - resetFgsRestrictionLocked(r); ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), @@ -2160,7 +2118,12 @@ public void run() { } } - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false); + if (!s.mAllowWhileInUsePermissionInFgs) { + s.mAllowWhileInUsePermissionInFgs = + shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, + callingPid, callingUid, + service, s, false); + } if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -3456,7 +3419,7 @@ private final void bringDownServiceLocked(ServiceRecord r) { r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; - resetFgsRestrictionLocked(r); + r.mAllowWhileInUsePermissionInFgs = false; // Clear start entries. r.clearDeliveredStartsLocked(); @@ -4937,7 +4900,7 @@ private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw, * @return true if allow, false otherwise. */ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, - int callingPid, int callingUid, ServiceRecord r, + int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { // Is the background FGS start restriction turned on? if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { @@ -5019,32 +4982,4 @@ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage } return false; } - - boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid, - String callingPackage) { - return shouldAllowWhileInUsePermissionInFgsLocked( - callingPackage, callingPid, callingUid, null, false); - } - - /** - * In R, mAllowWhileInUsePermissionInFgs is to 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 r the service to start. - * @return true if allow, false otherwise. - */ - private void setFgsRestrictionLocked(String callingPackage, - int callingPid, int callingUid, ServiceRecord r, - boolean allowBackgroundActivityStarts) { - r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime(); - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked( - callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); - } - } - - private void resetFgsRestrictionLocked(ServiceRecord r) { - r.mAllowWhileInUsePermissionInFgs = false; - } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 00d8208ea118..7be843f17863 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -88,7 +88,6 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_PROCESS_START_ASYNC = "process_start_async"; static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; - static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -122,7 +121,6 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_PROCESS_START_ASYNC = true; private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; - private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; // Flag stored in the DeviceConfig API. @@ -275,12 +273,6 @@ final class ActivityManagerConstants extends ContentObserver { // this long. public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; - /** - * When service started from background, before the timeout it can be promoted to FGS by calling - * Service.startForeground(). - */ - volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS; - // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; @@ -429,9 +421,6 @@ public void onPropertiesChanged(Properties properties) { case KEY_MIN_ASSOC_LOG_DURATION: updateMinAssocLogDuration(); break; - case KEY_FGS_START_FOREGROUND_TIMEOUT: - updateFgsStartForegroundTimeout(); - break; default: break; } @@ -708,13 +697,6 @@ private void updateMinAssocLogDuration() { /* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION); } - private void updateFgsStartForegroundTimeout() { - mFgsStartForegroundTimeoutMs = DeviceConfig.getLong( - DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - KEY_FGS_START_FOREGROUND_TIMEOUT, - DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS); - } - void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":"); @@ -787,8 +769,6 @@ void dump(PrintWriter pw) { pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray())); pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("="); pw.println(MIN_ASSOC_LOG_DURATION); - pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("="); - pw.println(mFgsStartForegroundTimeoutMs); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 0e628289a09f..1b65dbac2294 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -142,10 +142,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; - // The number of times Service.startForeground() is called; - int mStartForegroundCount; - // Last time mAllowWhileInUsePermissionInFgs is set. - long mLastSetFgsRestrictionTime; // the most recent package that start/bind this service. String mRecentCallingPackage; @@ -410,8 +406,6 @@ void dump(PrintWriter pw, String prefix) { } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); - pw.print(prefix); pw.print("startForegroundCount="); - pw.println(mStartForegroundCount); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); if (delayed) { From c2175a1fcb44409451145382255b081263ca5f38 Mon Sep 17 00:00:00 2001 From: Winson Date: Fri, 18 Jun 2021 11:17:50 -0700 Subject: [PATCH 003/208] Fix parsing code parcelling errors Address problems reading/writing: - ParsingPackageImpl mKeySetMapping - ParsingPackageImpl mQueriesIntent Bug: 187043377 Bug: 195962697 Test: atest com.android.server.pm.test.parsing.parcelling Merged-In: I5b33315f8248d5fcbdef2cc04ecf77cc18dbd7b6 Change-Id: I5b33315f8248d5fcbdef2cc04ecf77cc18dbd7b6 (cherry picked from commit f93af7ef7ebe9d139a34e615b97393a41ebabb56) (cherry picked from commit 37a0b6de89f7fb321fbeac02ec1a012817b8e682) --- .../pm/parsing/ParsingPackageImpl.java | 6 +- .../pm/parsing/ParsingPackageUtils.java | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 2da93ca34019..295107a06f99 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -1007,7 +1007,7 @@ public void writeToParcel(Parcel dest, int flags) { sForInternedStringList.parcel(this.requestedPermissions, dest, flags); sForInternedStringList.parcel(this.implicitPermissions, dest, flags); sForStringSet.parcel(this.upgradeKeySets, dest, flags); - dest.writeMap(this.keySetMapping); + ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping); sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags); dest.writeTypedList(this.activities); dest.writeTypedList(this.receivers); @@ -1026,7 +1026,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeBoolean(this.use32BitAbi); dest.writeBoolean(this.visibleToInstantApps); dest.writeBoolean(this.forceQueryable); - dest.writeParcelableList(this.queriesIntents, flags); + dest.writeTypedList(this.queriesIntents, flags); sForInternedStringList.parcel(this.queriesPackages, dest, flags); sForInternedStringSet.parcel(this.queriesProviders, dest, flags); dest.writeString(this.appComponentFactory); @@ -1169,7 +1169,7 @@ public ParsingPackageImpl(Parcel in) { this.requestedPermissions = sForInternedStringList.unparcel(in); this.implicitPermissions = sForInternedStringList.unparcel(in); this.upgradeKeySets = sForStringSet.unparcel(in); - this.keySetMapping = in.readHashMap(boot); + this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in); this.protectedBroadcasts = sForInternedStringList.unparcel(in); this.activities = in.createTypedArrayList(ParsedActivity.CREATOR); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index ab0ed51fb909..a56a3ea9b2f6 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -84,6 +84,7 @@ import android.os.Build; import android.os.Bundle; import android.os.FileUtils; +import android.os.Parcel; import android.os.RemoteException; import android.os.Trace; import android.os.ext.SdkExtensions; @@ -2834,6 +2835,68 @@ private static String nonResString(@StyleableRes int index, TypedArray sa) { return sa.getNonResourceString(index); } + /** + * Writes the keyset mapping to the provided package. {@code null} mappings are permitted. + */ + public static void writeKeySetMapping(@NonNull Parcel dest, + @NonNull Map> keySetMapping) { + if (keySetMapping == null) { + dest.writeInt(-1); + return; + } + + final int N = keySetMapping.size(); + dest.writeInt(N); + + for (String key : keySetMapping.keySet()) { + dest.writeString(key); + ArraySet keys = keySetMapping.get(key); + if (keys == null) { + dest.writeInt(-1); + continue; + } + + final int M = keys.size(); + dest.writeInt(M); + for (int j = 0; j < M; j++) { + dest.writeSerializable(keys.valueAt(j)); + } + } + } + + /** + * Reads a keyset mapping from the given parcel at the given data position. May return + * {@code null} if the serialized mapping was {@code null}. + */ + @NonNull + public static ArrayMap> readKeySetMapping(@NonNull Parcel in) { + final int N = in.readInt(); + if (N == -1) { + return null; + } + + ArrayMap> keySetMapping = new ArrayMap<>(); + for (int i = 0; i < N; ++i) { + String key = in.readString(); + final int M = in.readInt(); + if (M == -1) { + keySetMapping.put(key, null); + continue; + } + + ArraySet keys = new ArraySet<>(M); + for (int j = 0; j < M; ++j) { + PublicKey pk = (PublicKey) in.readSerializable(); + keys.add(pk); + } + + keySetMapping.put(key, keys); + } + + return keySetMapping; + } + + /** * Callback interface for retrieving information that may be needed while parsing * a package. From 5ebcf7638b93fef29839846ecfbfca1b3ddede06 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 18 Aug 2021 16:35:00 -0700 Subject: [PATCH 004/208] Bluetooth: Fix formatting in getAlias() Bug: 180747689 Test: manual Change-Id: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 (cherry picked from commit 3c4a917cb9481f4b70c26b6a06e798e65e5895ce) --- core/java/android/bluetooth/BluetoothDevice.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 4c595a9cb140..b1c7d6eadbba 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1230,7 +1230,10 @@ public String getAlias() { if (alias == null) { return getName(); } - return alias; + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } From 1922418093696a2d3dd9d675cd7cbff9616daba7 Mon Sep 17 00:00:00 2001 From: Evan Chen Date: Thu, 2 Sep 2021 22:05:51 +0000 Subject: [PATCH 005/208] Backporting the change of ag/15629060 to rvc-dev Bug: 172251622 Bug: 197035186 Change-Id: Ic3ffcb622af232473adf8a8c8307cce78227bac0 Merged-In: I7e2f95fd85e18127dc481f188ace8ad6effc4831 Test: Manual (cherry picked from commit 7f687d49115016a992f86c4167222804348dc2d5) --- .../companiondevicemanager/DeviceChooserActivity.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index 5ac059be2010..ac15164c4174 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -66,8 +66,8 @@ public void onCreate(Bundle savedInstanceState) { final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); setTitle(Html.fromHtml(getString( R.string.confirmation_title, - getCallingAppName(), - selectedDevice.getDisplayName()), 0)); + Html.escapeHtml(getCallingAppName()), + Html.escapeHtml(selectedDevice.getDisplayName())), 0)); mPairButton = findViewById(R.id.button_pair); mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice)); getService().mSelectedDevice = selectedDevice; @@ -76,7 +76,8 @@ public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.device_chooser); mPairButton = findViewById(R.id.button_pair); mPairButton.setVisibility(View.GONE); - setTitle(Html.fromHtml(getString(R.string.chooser_title, getCallingAppName()), 0)); + setTitle(Html.fromHtml(getString(R.string.chooser_title, + Html.escapeHtml(getCallingAppName())), 0)); mDeviceListView = findViewById(R.id.device_list); final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter; mDeviceListView.setAdapter(adapter); From 14572eefe7f3b65d44fb797cda6a02a8e67e6803 Mon Sep 17 00:00:00 2001 From: shubang Date: Fri, 13 Aug 2021 15:42:15 -0700 Subject: [PATCH 006/208] TIF: fix issue of using caller-aware methods after clearCallingIdentity() Bug: 189824175 Test: atest android.media.tv.cts.TvInputManagerTest Change-Id: Iced6c2245c0099bc4bcdaceb51f8cce4dbc0a392 (cherry picked from commit a2ab2772d70984b1c24d5d4509b9c7a5abea71f9) (cherry picked from commit 05363ef193ae231fc7a1fe666de88861e1084b17) --- .../java/com/android/server/tv/TvInputManagerService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 323ac7b8806e..4668741aed0b 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -2005,10 +2005,9 @@ private void ensureCaptureTvInputPermission() { public void requestChannelBrowsable(Uri channelUri, int userId) throws RemoteException { final String callingPackageName = getCallingPackageName(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), + Binder.getCallingUid(), userId, "requestChannelBrowsable"); final long identity = Binder.clearCallingIdentity(); - final int callingUid = Binder.getCallingUid(); - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, - userId, "requestChannelBrowsable"); try { Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED); List list = getContext().getPackageManager() From 33e1edf3e9c5e5181756cea92dceb4d399ca0472 Mon Sep 17 00:00:00 2001 From: kholoud mohamed Date: Tue, 4 May 2021 16:33:54 +0100 Subject: [PATCH 007/208] Changed INTERACT_ACROSS_PROFILES appop to be set per UID In some cases, enforceCrossUserOrProfilePermission fails for packages which have INTERACT_ACROSS_PROFILE appop granted if it shares the uid with another package that doesn't hold the permission, this is because getPackagesForUid is used to get the callingPackage which could return either packages randomly. I've changed setting the appop to be per uid instead of per package, Test: manual testing Bug: 183188804 Bug: 183730243 Bug: 195630721 Change-Id: I7a72c1d3abd1f83924865326797630ded2f2040f Merged-In: I7a72c1d3abd1f83924865326797630ded2f2040f (cherry picked from commit 4670d1d855a853852980148d99b190171db4ec79) (cherry picked from commit be1752c13f694de4d637870214e0124d43087302) --- .../pm/CrossProfileAppsServiceImpl.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 3e9e45e59b95..83355e704e6e 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -482,16 +482,16 @@ private void setInteractAcrossProfilesAppOpForProfileOrThrow( // this particular app-op to be modified without the broader app-op permissions. mInjector.withCleanCallingIdentity(() -> mInjector.getAppOpsManager() - .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode)); + .setUidMode(OP_INTERACT_ACROSS_PROFILES, uid, newMode)); } else { mInjector.getAppOpsManager() - .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode); + .setUidMode(OP_INTERACT_ACROSS_PROFILES, uid, newMode); } // Kill the UID before sending the broadcast to ensure that apps can be informed when // their app-op has been revoked. maybeKillUid(packageName, uid, hadPermission); - sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(profileId)); - maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, logMetrics, uid); + sendCanInteractAcrossProfilesChangedBroadcast(packageName, UserHandle.of(profileId)); + maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, profileId, logMetrics); } /** @@ -509,7 +509,7 @@ private void maybeKillUid(String packageName, int uid, boolean hadPermission) { } private void maybeLogSetInteractAcrossProfilesAppOp( - String packageName, @Mode int newMode, boolean logMetrics, int uid) { + String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics) { if (!logMetrics) { return; } @@ -517,7 +517,7 @@ private void maybeLogSetInteractAcrossProfilesAppOp( .createEvent(DevicePolicyEnums.SET_INTERACT_ACROSS_PROFILES_APP_OP) .setStrings(packageName) .setInt(newMode) - .setBoolean(appDeclaresCrossProfileAttribute(uid)) + .setBoolean(appDeclaresCrossProfileAttribute(packageName)) .write(); } @@ -533,10 +533,10 @@ private boolean currentModeEquals(@Mode int otherMode, String packageName, int u } private void sendCanInteractAcrossProfilesChangedBroadcast( - String packageName, int uid, UserHandle userHandle) { + String packageName, UserHandle userHandle) { final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED).setPackage(packageName); - if (appDeclaresCrossProfileAttribute(uid)) { + if (appDeclaresCrossProfileAttribute(packageName)) { intent.addFlags( Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); } else { @@ -553,8 +553,8 @@ private List findBroadcastReceiversForUser(Intent intent, UserHandl .queryBroadcastReceiversAsUser(intent, /* flags= */ 0, userHandle); } - private boolean appDeclaresCrossProfileAttribute(int uid) { - return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile(); + private boolean appDeclaresCrossProfileAttribute(String packageName) { + return mInjector.getPackageManagerInternal().getPackage(packageName).isCrossProfile(); } @Override From 21daffd10a23b304255a165e1a82892be32d8caf Mon Sep 17 00:00:00 2001 From: "Kevin F. Haggerty" Date: Wed, 3 Nov 2021 07:00:35 -0600 Subject: [PATCH 008/208] Revert "Revert "BG-FGS-start while-in-use permission restriction improve..."" * Change ebfad50cc1450 ("Allow FGS started from MediaButtonReceiver to have while-in-use permissions.") make use of canAllowWhileInUsePermissionInFgsLocked, which the noted change removes. This reverts commit a95cfc3ed8cfe22fbc1a25f419dd2dce1c4f5bcb. Change-Id: If516d7e8c775f527484cfef4d69b0bef6b4397ff --- .../com/android/server/am/ActiveServices.java | 107 ++++++++++++++---- .../server/am/ActivityManagerConstants.java | 20 ++++ .../com/android/server/am/ServiceRecord.java | 6 + 3 files changed, 112 insertions(+), 21 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index e4dd8756fe62..1cd7115f8edc 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -734,11 +734,8 @@ ComponentName startServiceLocked(IApplicationThread caller, Intent service, Stri } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, - callingUid, service, r, allowBackgroundActivityStarts); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r, + allowBackgroundActivityStarts); return cmp; } @@ -1411,14 +1408,6 @@ private void setServiceForegroundInnerLocked(final ServiceRecord r, int id, + String.format("0x%08X", manifestType) + " in service element of manifest file"); } - // If the foreground service is not started from TOP process, do not allow it to - // have while-in-use location/camera/microphone access. - if (!r.mAllowWhileInUsePermissionInFgs) { - Slog.w(TAG, - "Foreground service started from background can not have " - + "location/camera/microphone access: service " - + r.shortInstanceName); - } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; @@ -1466,6 +1455,57 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { ignoreForeground = true; } + if (!ignoreForeground) { + if (r.mStartForegroundCount == 0) { + /* + If the service was started with startService(), not + startForegroundService(), and if startForeground() isn't called within + mFgsStartForegroundTimeoutMs, then we check the state of the app + (who owns the service, which is the app that called startForeground()) + again. If the app is in the foreground, or in any other cases where + FGS-starts are allowed, then we still allow the FGS to be started. + Otherwise, startForeground() would fail. + + If the service was started with startForegroundService(), then the service + must call startForeground() within a timeout anyway, so we don't need this + check. + */ + if (!r.fgRequired) { + final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime; + if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { + resetFgsRestrictionLocked(r); + setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, + r.appInfo.uid, r, false); + EventLog.writeEvent(0x534e4554, "183147114", + r.appInfo.uid, + "call setFgsRestrictionLocked again due to " + + "startForegroundTimeout"); + } + } + } else if (r.mStartForegroundCount >= 1) { + // The second or later time startForeground() is called after service is + // started. Check for app state again. + final long delayMs = SystemClock.elapsedRealtime() - + r.mLastSetFgsRestrictionTime; + if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { + resetFgsRestrictionLocked(r); + setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, + r.appInfo.uid, r, false); + EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid, + "call setFgsRestrictionLocked for " + + (r.mStartForegroundCount + 1) + "th startForeground"); + } + } + // If the foreground service is not started from TOP process, do not allow it to + // have while-in-use location/camera/microphone access. + if (!r.mAllowWhileInUsePermissionInFgs) { + Slog.w(TAG, + "Foreground service started from background can not have " + + "location/camera/microphone access: service " + + r.shortInstanceName); + } + } + // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -1501,6 +1541,7 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { active.mNumActive++; } r.isForeground = true; + r.mStartForegroundCount++; if (!stopProcStatsOp) { ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1559,6 +1600,7 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { decActiveForegroundAppLocked(smap, r); } r.isForeground = false; + resetFgsRestrictionLocked(r); ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), @@ -2118,12 +2160,7 @@ public void run() { } } - if (!s.mAllowWhileInUsePermissionInFgs) { - s.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - callingPid, callingUid, - service, s, false); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false); if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -3419,7 +3456,7 @@ private final void bringDownServiceLocked(ServiceRecord r) { r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; - r.mAllowWhileInUsePermissionInFgs = false; + resetFgsRestrictionLocked(r); // Clear start entries. r.clearDeliveredStartsLocked(); @@ -4900,7 +4937,7 @@ private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw, * @return true if allow, false otherwise. */ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, - int callingPid, int callingUid, Intent intent, ServiceRecord r, + int callingPid, int callingUid, ServiceRecord r, boolean allowBackgroundActivityStarts) { // Is the background FGS start restriction turned on? if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { @@ -4982,4 +5019,32 @@ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage } return false; } + + boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid, + String callingPackage) { + return shouldAllowWhileInUsePermissionInFgsLocked( + callingPackage, callingPid, callingUid, null, false); + } + + /** + * In R, mAllowWhileInUsePermissionInFgs is to 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 r the service to start. + * @return true if allow, false otherwise. + */ + private void setFgsRestrictionLocked(String callingPackage, + int callingPid, int callingUid, ServiceRecord r, + boolean allowBackgroundActivityStarts) { + r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime(); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked( + callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); + } + } + + private void resetFgsRestrictionLocked(ServiceRecord r) { + r.mAllowWhileInUsePermissionInFgs = false; + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 7be843f17863..00d8208ea118 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -88,6 +88,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_PROCESS_START_ASYNC = "process_start_async"; static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; + static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -121,6 +122,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_PROCESS_START_ASYNC = true; private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; + private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; // Flag stored in the DeviceConfig API. @@ -273,6 +275,12 @@ final class ActivityManagerConstants extends ContentObserver { // this long. public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; + /** + * When service started from background, before the timeout it can be promoted to FGS by calling + * Service.startForeground(). + */ + volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS; + // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; @@ -421,6 +429,9 @@ public void onPropertiesChanged(Properties properties) { case KEY_MIN_ASSOC_LOG_DURATION: updateMinAssocLogDuration(); break; + case KEY_FGS_START_FOREGROUND_TIMEOUT: + updateFgsStartForegroundTimeout(); + break; default: break; } @@ -697,6 +708,13 @@ private void updateMinAssocLogDuration() { /* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION); } + private void updateFgsStartForegroundTimeout() { + mFgsStartForegroundTimeoutMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_FOREGROUND_TIMEOUT, + DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS); + } + void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":"); @@ -769,6 +787,8 @@ void dump(PrintWriter pw) { pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray())); pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("="); pw.println(MIN_ASSOC_LOG_DURATION); + pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("="); + pw.println(mFgsStartForegroundTimeoutMs); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 1b65dbac2294..0e628289a09f 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -142,6 +142,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; + // The number of times Service.startForeground() is called; + int mStartForegroundCount; + // Last time mAllowWhileInUsePermissionInFgs is set. + long mLastSetFgsRestrictionTime; // the most recent package that start/bind this service. String mRecentCallingPackage; @@ -406,6 +410,8 @@ void dump(PrintWriter pw, String prefix) { } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); + pw.print(prefix); pw.print("startForegroundCount="); + pw.println(mStartForegroundCount); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); if (delayed) { From 1a43f66f1382d3bee0e30f1d64fd37df9b0479f2 Mon Sep 17 00:00:00 2001 From: cnchen Date: Mon, 6 Jul 2020 19:58:44 +0800 Subject: [PATCH 009/208] Fix NPE on getTitle() Bug: 199922685 Test: make RunSettingsLibRoboTests -j40 Change-Id: I35bf6f5911dc0ee56e43af7faddaceb479362863 (cherry picked from commit f00f2203b578e919e56f21f3a4119a974b942243) Merged-In: I35bf6f5911dc0ee56e43af7faddaceb479362863 (cherry picked from commit c006ad915aea348043446a45cc019f522c83928c) --- .../src/com/android/settingslib/wifi/AccessPoint.java | 10 ++++++---- .../com/android/settingslib/wifi/AccessPointTest.java | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 8968340b65f4..c5e66bef9653 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -1117,14 +1117,16 @@ public String getSavedNetworkSummary() { * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title. */ public String getTitle() { - if (isPasspoint()) { + if (isPasspoint() && !TextUtils.isEmpty(mConfig.providerFriendlyName)) { return mConfig.providerFriendlyName; - } else if (isPasspointConfig()) { + } else if (isPasspointConfig() && !TextUtils.isEmpty(mProviderFriendlyName)) { return mProviderFriendlyName; - } else if (isOsuProvider()) { + } else if (isOsuProvider() && !TextUtils.isEmpty(mOsuProvider.getFriendlyName())) { return mOsuProvider.getFriendlyName(); - } else { + } else if (!TextUtils.isEmpty(getSsidStr())) { return getSsidStr(); + } else { + return ""; } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index bcabec858487..46ecbd45a860 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -147,6 +147,17 @@ public void testSsidIsSpannableString_returnFalse() { assertThat(ssid instanceof SpannableString).isFalse(); } + @Test + public void testCompareTo_GivesNull() { + WifiConfiguration spyConfig = spy(new WifiConfiguration()); + + when(spyConfig.isPasspoint()).thenReturn(true); + spyConfig.providerFriendlyName = null; + AccessPoint passpointAp = new AccessPoint(mContext, spyConfig); + + assertThat(passpointAp.getTitle()).isEqualTo(""); + } + @Test public void testCompareTo_GivesActiveBeforeInactive() { AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build(); From 4122be11f244d49e3fd7ea0a32a7a042c1132983 Mon Sep 17 00:00:00 2001 From: Yu-Han Yang Date: Tue, 21 Sep 2021 12:29:34 -0700 Subject: [PATCH 010/208] Fix serialization bug in GpsNavigationMessage Bug: 196970023 Test: presubmits passing. Change-Id: I69f51eb2faac0cf2ee9f7a5f94f7100925f7221c (cherry picked from commit 1903539154df194722ce5f0dbbd38e18f8c1fc12) --- location/java/android/location/GpsNavigationMessage.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java index dc1e99fd6a4f..2b978f759a13 100644 --- a/location/java/android/location/GpsNavigationMessage.java +++ b/location/java/android/location/GpsNavigationMessage.java @@ -262,12 +262,8 @@ public GpsNavigationMessage createFromParcel(Parcel parcel) { parcel.readByteArray(data); navigationMessage.setData(data); - if (parcel.dataAvail() >= Integer.SIZE) { - int status = parcel.readInt(); - navigationMessage.setStatus((short) status); - } else { - navigationMessage.setStatus(STATUS_UNKNOWN); - } + int status = parcel.readInt(); + navigationMessage.setStatus((short) status); return navigationMessage; } From cf90a73c4ccb8e2927c19dc7c902179e0d8f6fe2 Mon Sep 17 00:00:00 2001 From: Jeff DeCew Date: Fri, 17 Sep 2021 16:32:58 -0400 Subject: [PATCH 011/208] Do not show the notification footer until the user is set up. Bug: 193149550 Test: follow repro steps Merged-In: I49e2b8bcec7b2ce0a9776ff30a64c07f24949da7 Change-Id: I49e2b8bcec7b2ce0a9776ff30a64c07f24949da7 (cherry picked from commit 73c3c0a7d649b725da0459cc55c6418e0f2ba175) --- .../stack/NotificationStackScrollLayout.java | 39 +++++++++++++++++++ .../NotificationStackScrollLayoutTest.java | 27 +++++++++++++ 2 files changed, 66 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index df687d71efa2..88cdbf9daad2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -157,6 +157,8 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; import com.android.systemui.statusbar.policy.ZenModeController; @@ -300,6 +302,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mExpandedInThisMotion; private boolean mShouldShowShelfOnly; protected boolean mScrollingEnabled; + private boolean mIsCurrentUserSetup; protected FooterView mFooterView; protected EmptyShadeView mEmptyShadeView; private boolean mDismissAllInProgress; @@ -509,6 +512,8 @@ public void getOutline(View view, Outline outline) { private final NotifPipeline mNotifPipeline; private final NotifCollection mNotifCollection; private final NotificationEntryManager mEntryManager; + private final DeviceProvisionedController mDeviceProvisionedController = + Dependency.get(DeviceProvisionedController.class); private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @VisibleForTesting @@ -649,6 +654,29 @@ public NotificationStackScrollLayout( }, HIGH_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL, Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + mDeviceProvisionedController.addCallback( + new DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + updateCurrentUserIsSetup(); + } + + @Override + public void onUserSwitched() { + updateCurrentUserIsSetup(); + } + + @Override + public void onUserSetupChanged() { + updateCurrentUserIsSetup(); + } + + private void updateCurrentUserIsSetup() { + setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup()); + } + }); + + mFeatureFlags = featureFlags; mNotifPipeline = notifPipeline; mEntryManager = entryManager; @@ -776,6 +804,7 @@ public void updateFooter() { boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL); mStatusBar.setHasClearableNotifs(hasActiveClearableNotifications(ROWS_ALL)); boolean showFooterView = (showDismissView || hasActiveNotifications()) + && mIsCurrentUserSetup // see: b/193149550 && mStatusBarState != StatusBarState.KEYGUARD && !mRemoteInputManager.getController().isRemoteInputActive(); boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(), @@ -5949,6 +5978,16 @@ public float calculateAppearFractionBypass() { return MathUtils.smoothStep(0, totalDistance, dragDownAmount); } + /** + * Sets whether the current user is set up, which is required to show the footer (b/193149550) + */ + public void setCurrentUserSetup(boolean isCurrentUserSetup) { + if (mIsCurrentUserSetup != isCurrentUserSetup) { + mIsCurrentUserSetup = isCurrentUserSetup; + updateFooter(); + } + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 2ae4caeca963..ba3d95f9b178 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -354,6 +354,7 @@ public void testInflateFooterView() { @Test public void testUpdateFooter_noNotifications() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); assertEquals(0, mEntryManager.getActiveNotificationsCount()); FooterView view = mock(FooterView.class); @@ -365,6 +366,8 @@ public void testUpdateFooter_noNotifications() { @Test public void testUpdateFooter_remoteInput() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); + ArrayList entries = new ArrayList<>(); entries.add(new NotificationEntryBuilder().build()); addEntriesToEntryManager(entries); @@ -384,6 +387,7 @@ public void testUpdateFooter_remoteInput() { @Test public void testUpdateFooter_oneClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); ArrayList entries = new ArrayList<>(); entries.add(new NotificationEntryBuilder().build()); @@ -400,9 +404,30 @@ public void testUpdateFooter_oneClearableNotification() { verify(mStackScroller).updateFooterView(true, true, true); } + @Test + public void testUpdateFooter_oneClearableNotification_beforeUserSetup() { + setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(false); + + ArrayList entries = new ArrayList<>(); + entries.add(new NotificationEntryBuilder().build()); + addEntriesToEntryManager(entries); + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); + when(row.canViewBeDismissed()).thenReturn(true); + when(mStackScroller.getChildCount()).thenReturn(1); + when(mStackScroller.getChildAt(anyInt())).thenReturn(row); + + FooterView view = mock(FooterView.class); + mStackScroller.setFooterView(view); + mStackScroller.updateFooter(); + verify(mStackScroller).updateFooterView(false, true, true); + } + @Test public void testUpdateFooter_oneNonClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); ArrayList entries = new ArrayList<>(); entries.add(new NotificationEntryBuilder().build()); @@ -416,6 +441,8 @@ public void testUpdateFooter_oneNonClearableNotification() { @Test public void testUpdateFooter_atEnd() { + mStackScroller.setCurrentUserSetup(true); + // add footer mStackScroller.inflateFooterView(); From b7d18fb205d14fbd892a351c7aa9d8a87a5f39cd Mon Sep 17 00:00:00 2001 From: Janis Danisevskis Date: Thu, 23 Sep 2021 16:56:06 -0700 Subject: [PATCH 012/208] DO NOT MERGE KeyStore ChunkedStreamer must tolerate update consuming 0 bytes. Some older versions of Keymster do not consume any data in certain block modes unless a full block of data was presented. Bug: 199032140 Bug: 200041882 Test: CtsKeyStoreTestcases with KM1.0 backend. Merged-In: Ic2da6e5c12628f11603f51c7a3408aad70947d95 Change-Id: Ic2da6e5c12628f11603f51c7a3408aad70947d95 (cherry picked from commit 395e56a71c87d3748ca280f235da04983069de87) --- ...eyStoreCryptoOperationChunkedStreamer.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java index 2c0f40d528d2..cedc4d2fb8e6 100644 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java @@ -126,8 +126,8 @@ public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyS if (mChunkLength > mChunkSizeMax) { throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, - "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax - + " Actual: " + mChunkLength); + "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax + + " Actual: " + mChunkLength); } if (mChunkLength >= mChunkSizeThreshold) { @@ -139,18 +139,28 @@ public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyS } else if (opResult.resultCode != KeyStore.NO_ERROR) { throw KeyStore.getKeyStoreException(opResult.resultCode); } - if (opResult.inputConsumed <= 0) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, - "Keystore consumed 0 of " + mChunkLength + " bytes provided."); - } else if (opResult.inputConsumed > mChunkLength) { + if (opResult.inputConsumed == 0) { + // Some KM implementations do not consume data in certain block modes unless a + // full block of data was presented. + if (inputLength > 0) { + // More input is available, but it wasn't included into the previous chunk + // because the chunk reached its maximum permitted size. + // Shouldn't have happened. + throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, + "Keystore consumed nothing from max-sized chunk: " + mChunkLength + + " bytes"); + } + } else if (opResult.inputConsumed > mChunkLength || opResult.inputConsumed < 0) { throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, - "Keystore consumed more input than provided. Provided: " - + mChunkLength + ", consumed: " + opResult.inputConsumed); + "Keystore consumed more input than provided (or inputConsumed was " + + "negative." + + " Provided: " + mChunkLength + + ", consumed: " + opResult.inputConsumed); } mChunkLength -= opResult.inputConsumed; if (mChunkLength > 0) { - // Partialy consumed, shift chunk contents + // Partially consumed, shift chunk contents ArrayUtils.copy(mChunk, opResult.inputConsumed, mChunk, 0, mChunkLength); } From 3f45961cc13f17f49e1872f6cd0c605326b4cc8c Mon Sep 17 00:00:00 2001 From: Collin Fijalkovich Date: Mon, 5 Oct 2020 13:10:33 -0700 Subject: [PATCH 013/208] Disallow overlays for ResolverActivity Prevents non-system apps from placing a window over the app selection screen. Bug: 143559931 Test: Installed test app and attempted to overlay Change-Id: Ied05088a5007e0f10cd3e1abd8d7da8ffeb3b674 Merged-In: Ied05088a5007e0f10cd3e1abd8d7da8ffeb3b674 (cherry picked from commit 34534e1fd2057ea6d858ce82f8505cbdb1026d9a) (cherry picked from commit 978809e428bf44be1f9a86d1284aa61103b6f1cf) --- .../com/android/internal/app/ResolverActivity.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 1270185e1ea5..c5b84f7c9c83 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -19,6 +19,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_PROFILES; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.PermissionChecker.PID_UNKNOWN; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.annotation.Nullable; import android.annotation.StringRes; @@ -69,7 +70,9 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.Window; import android.view.WindowInsets; +import android.view.WindowManager; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.Button; @@ -101,7 +104,6 @@ import java.util.Objects; import java.util.Set; - /** * This activity is displayed when the system attempts to start an Intent for * which there is more than one matching activity, allowing the user to decide @@ -822,6 +824,8 @@ protected void onRestart() { @Override protected void onStart() { super.onStart(); + + this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); if (shouldShowTabs()) { mWorkProfileStateReceiver = createWorkProfileStateReceiver(); registerWorkProfileStateReceiver(); @@ -849,6 +853,12 @@ private void registerWorkProfileStateReceiver() { @Override protected void onStop() { super.onStop(); + + final Window window = this.getWindow(); + final WindowManager.LayoutParams attrs = window.getAttributes(); + attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + window.setAttributes(attrs); + if (mRegistered) { mPersonalPackageMonitor.unregister(); if (mWorkPackageMonitor != null) { From 4852fa4545d407de3493222e0a452f7790614ef8 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 20 Sep 2021 20:14:46 -0700 Subject: [PATCH 014/208] Import translations. DO NOT MERGE ANYWHERE BUG:179338675 Auto-generated-cl: translation import Change-Id: I12b153342014721d1ebd202be933fdc54978e0f2 (cherry picked from commit 15331bfdd2ad22cf1636720f7b72c14a98dfb18a) (cherry picked from commit 14eba599b42ec12f72e2dfc07313db9d8d9f70ae) --- core/res/res/values-af/strings.xml | 1 + core/res/res/values-am/strings.xml | 1 + core/res/res/values-ar/strings.xml | 1 + core/res/res/values-as/strings.xml | 1 + core/res/res/values-az/strings.xml | 1 + core/res/res/values-b+sr+Latn/strings.xml | 1 + core/res/res/values-be/strings.xml | 1 + core/res/res/values-bg/strings.xml | 1 + core/res/res/values-bn/strings.xml | 1 + core/res/res/values-bs/strings.xml | 1 + core/res/res/values-ca/strings.xml | 1 + core/res/res/values-cs/strings.xml | 1 + core/res/res/values-da/strings.xml | 1 + core/res/res/values-de/strings.xml | 1 + core/res/res/values-el/strings.xml | 1 + core/res/res/values-en-rAU/strings.xml | 1 + core/res/res/values-en-rCA/strings.xml | 1 + core/res/res/values-en-rGB/strings.xml | 1 + core/res/res/values-en-rIN/strings.xml | 1 + core/res/res/values-en-rXC/strings.xml | 1 + core/res/res/values-es-rUS/strings.xml | 1 + core/res/res/values-es/strings.xml | 1 + core/res/res/values-et/strings.xml | 1 + core/res/res/values-eu/strings.xml | 1 + core/res/res/values-fa/strings.xml | 1 + core/res/res/values-fi/strings.xml | 1 + core/res/res/values-fr-rCA/strings.xml | 1 + core/res/res/values-fr/strings.xml | 1 + core/res/res/values-gl/strings.xml | 1 + core/res/res/values-gu/strings.xml | 1 + core/res/res/values-hi/strings.xml | 1 + core/res/res/values-hr/strings.xml | 1 + core/res/res/values-hu/strings.xml | 1 + core/res/res/values-hy/strings.xml | 1 + core/res/res/values-in/strings.xml | 1 + core/res/res/values-is/strings.xml | 1 + core/res/res/values-it/strings.xml | 1 + core/res/res/values-iw/strings.xml | 1 + core/res/res/values-ja/strings.xml | 1 + core/res/res/values-ka/strings.xml | 1 + core/res/res/values-kk/strings.xml | 1 + core/res/res/values-km/strings.xml | 1 + core/res/res/values-kn/strings.xml | 1 + core/res/res/values-ko/strings.xml | 1 + core/res/res/values-ky/strings.xml | 1 + core/res/res/values-lo/strings.xml | 1 + core/res/res/values-lt/strings.xml | 1 + core/res/res/values-lv/strings.xml | 1 + core/res/res/values-mk/strings.xml | 1 + core/res/res/values-ml/strings.xml | 1 + core/res/res/values-mn/strings.xml | 1 + core/res/res/values-mr/strings.xml | 1 + core/res/res/values-ms/strings.xml | 1 + core/res/res/values-my/strings.xml | 1 + core/res/res/values-nb/strings.xml | 1 + core/res/res/values-ne/strings.xml | 1 + core/res/res/values-nl/strings.xml | 4 ++-- core/res/res/values-or/strings.xml | 1 + core/res/res/values-pa/strings.xml | 1 + core/res/res/values-pl/strings.xml | 4 ++-- core/res/res/values-pt-rBR/strings.xml | 1 + core/res/res/values-pt-rPT/strings.xml | 1 + core/res/res/values-pt/strings.xml | 1 + core/res/res/values-ro/strings.xml | 1 + core/res/res/values-ru/strings.xml | 1 + core/res/res/values-si/strings.xml | 1 + core/res/res/values-sk/strings.xml | 1 + core/res/res/values-sl/strings.xml | 1 + core/res/res/values-sq/strings.xml | 1 + core/res/res/values-sr/strings.xml | 1 + core/res/res/values-sv/strings.xml | 1 + core/res/res/values-sw/strings.xml | 1 + core/res/res/values-ta/strings.xml | 1 + core/res/res/values-te/strings.xml | 4 ++-- core/res/res/values-th/strings.xml | 1 + core/res/res/values-tl/strings.xml | 1 + core/res/res/values-tr/strings.xml | 1 + core/res/res/values-uk/strings.xml | 1 + core/res/res/values-ur/strings.xml | 1 + core/res/res/values-uz/strings.xml | 1 + core/res/res/values-vi/strings.xml | 1 + core/res/res/values-zh-rCN/strings.xml | 1 + core/res/res/values-zh-rHK/strings.xml | 1 + core/res/res/values-zh-rTW/strings.xml | 1 + core/res/res/values-zu/strings.xml | 1 + 85 files changed, 88 insertions(+), 6 deletions(-) diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 7d773d902bbe..55329e07ba91 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1413,6 +1413,7 @@ "Weier" "Toestemming versoek" "Toestemming versoek\nvir rekening %s." + "Toestemming versoek deur %1$s\nvir rekening %2$s" "Jy gebruik hierdie program buite jou werkprofiel" "Jy gebruik tans hierdie program in jou werkprofiel" "Invoermetode" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index d41868e8c478..670b64e010f0 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1413,6 +1413,7 @@ "ያስተባብሉ" "ፈቃድ ተጠይቋል" \n" ለ%s መለያ ፈቃድ ተጠይቋል" + "ለመለያ %2$s\nበ%1$s የተጠየቀ ፈቃድ።" "ከስራ መገለጫዎ ውጪ ሆነው መተግበሪያ እየተጠቀሙ ነው" "ይህን መተግበሪያ በእርስዎ የስራ መገለጫ ላይ እየተጠቀሙበት ነው" "ግቤት ስልት" diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 4a99ec8021e8..7e5460160a7d 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1493,6 +1493,7 @@ "رفض" "الإذن مطلوب" "الإذن مطلوب\nللحساب %s." + "طلب تطبيق %1$s الإذن بالدخول\nإلى حساب %2$s." "أنت تستخدم هذا التطبيق خارج ملفك الشخصي للعمل" "أنت تستخدم هذا التطبيق في ملفك الشخصي للعمل" "طريقة الإرسال" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 2ca98b4c5f22..3573ff5d6eb9 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1413,6 +1413,7 @@ "প্ৰত্যাখ্যান কৰক" "অনুমতি বিচাৰি অনুৰোধ কৰা হৈছে" "%s একাউণ্টৰ বাবে\nঅনুমতি বিচাৰি অনুৰোধ কৰা হৈছে।" + "%1$s%2$s একাউণ্টটো এক্সেছৰ \nঅনুমতি বিচাৰি অনুৰোধ জনাইছে।" "আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ বাহিৰত এই এপটো ব্যৱহাৰ কৰি আছে" "আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ ভিতৰত এই এপটো ব্যৱহাৰ কৰি আছে" "ইনপুট পদ্ধতি" diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 36c293c491c6..52c57cdcd112 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1413,6 +1413,7 @@ "Rədd et" "İcazə tələb olunur" \n" hesabı üçün%s icazə sorğusu göndərildi." + "%1$s\ntərəfindən %2$s hesabı üçün icazə tələb edilib." "Bu tətbiqi iş profilinizdən kənarda istifadə edirsiniz" "Bu tətbiqi iş profilinizdə istifadə edirsiniz" "Daxiletmə metodu" diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index daec63a9d5a5..1399c8fb9f37 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1433,6 +1433,7 @@ "Odbij" "Zatražena je dozvola" "Zatražena je dozvola\nza nalog %s" + "%1$s traži dozvolu \nza nalog %2$s." "Koristite ovu aplikaciju izvan poslovnog profila" "Koristite ovu aplikaciju na poslovnom profilu" "Metod unosa" diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 220c3520a1db..bd35a675d814 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1453,6 +1453,7 @@ "Адмовіць" "Дазвол запытаны" "Запытаны дазвол\nдля ўліковага запісу %s" + "Праграма \"%1$s\" запытвае дазвол\nдля ўліковага запісу %2$s." "Вы выкарыстоўваеце гэту праграму па-за межамі свайго працоўнага профілю" "Вы выкарыстоўваеце гэту праграму ў сваім працоўным профілі" "Метад уводу" diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 8d3a23c99e3b..5a4dc99e3845 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1413,6 +1413,7 @@ "Отказване" "Иска се разрешение" "Иска се разрешение\nза профила %s." + "Поискано е разрешение от %1$s\nза профила %2$s." "Използвате това приложение извън служебния си потребителски профил" "Използвате това приложение в служебния си потребителски профил" "Метод на въвеждане" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 9462be3f2f44..b91868e8f147 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1413,6 +1413,7 @@ "অস্বীকার করুন" "অনুমতির অনুরোধ করা হয়েছে" "%sঅ্যাকাউন্টের জন্য\nঅনুমতির অনুরোধ করা হয়েছে৷" + "%2$s অ্যাকাউন্টের জন্য %1$s\n থেকে অনুমতি চাওয়া হয়েছে।" "আপনি এই অ্যাপ্লিকেশানটি আপনার কর্মস্থলের প্রোফাইলের বাইরে ব্যবহার করছেন" "আপনি এই অ্যাপ্লিকেশানটি আপনার কর্মস্থলের প্রোফাইলে ব্যবহার করছেন" "ইনপুট পদ্ধতি" diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 7511455fc0ee..2c4f91ee339c 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1433,6 +1433,7 @@ "Odbij" "Upućen zahtjev za odobrenje" "Upućen zahtjev za dozvolu\nza račun %s." + "Odobrenje je zatražila aplikacija %1$s\nza račun %2$s." "Aplikaciju koristite van poslovnog profila" "Aplikaciju koristite u poslovnom profilu" "Način unosa" diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index ca45c00fb8b3..2b18f3f8a731 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1413,6 +1413,7 @@ "Denega" "Permís sol·licitat" "S\'ha sol·licitat permís\nper al compte %s." + "%1$s ha sol·licitat permís\nper accedir al compte %2$s." "Estàs utilitzant aquesta aplicació fora del perfil de treball." "Estàs utilitzant l\'aplicació al perfil de treball." "Mètode d\'introducció de text" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index a5f421390d44..fdbf1f80719c 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1453,6 +1453,7 @@ "Odepřít" "Požadováno oprávnění" "Požadováno oprávnění\npro účet %s." + "Aplikace %1$s požádala o přístup\nk účtu %2$s." "Tuto aplikaci používáte mimo svůj pracovní profil." "Tuto aplikaci používáte v pracovním profilu" "Metoda zadávání dat" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 09e2ea20cd92..70dfcb2fb354 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1413,6 +1413,7 @@ "Afvis" "Der er anmodet om tilladelse" "Der er anmodet om tilladelse\nfor kontoen %s." + "%1$s har anmodet om tilladelse\nfor kontoen %2$s." "Du bruger denne app uden for din arbejdsprofil" "Du bruger denne app i din arbejdsprofil" "Inputmetode" diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 627a3ab6a4fd..dfe712e9f42a 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1413,6 +1413,7 @@ "Ablehnen" "Berechtigung angefordert" "Berechtigung angefordert\nfür Konto %s" + "Berechtigung wurde angefordert von %1$s\nfür das Konto %2$s." "Du verwendest diese App außerhalb deines Arbeitsprofils" "Du verwendest diese App in deinem Arbeitsprofil." "Eingabemethode" diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 9435d34b562e..a8ac541b9840 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1413,6 +1413,7 @@ "Άρνηση" "Απαιτείται άδεια" "Ζητήθηκε άδεια\nγια τον λογαριασμό %s." + "Ζητήθηκε άδεια από την εφαρμογή %1$s\nγια πρόσβαση στον λογαριασμό %2$s." "Χρησιμοποιείτε αυτήν την εφαρμογή εκτός του προφίλ εργασίας σας" "Χρησιμοποιείτε αυτήν την εφαρμογή στο προφίλ εργασίας" "Μέθοδος εισόδου" diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index c748cc80cb23..a0a64c9c1999 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1413,6 +1413,7 @@ "Deny" "Permission requested" "Permission requested\nfor account %s." + "Permission requested by %1$s\nfor account %2$s." "You\'re using this app outside of your work profile" "You\'re using this app in your work profile" "Input Method" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index d6faf46ee74a..3a82c601090f 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1413,6 +1413,7 @@ "Deny" "Permission requested" "Permission requested\nfor account %s." + "Permission requested by %1$s\nfor account %2$s." "You\'re using this app outside of your work profile" "You\'re using this app in your work profile" "Input Method" diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 695ddb0e4e4e..bdeee0a9809f 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1413,6 +1413,7 @@ "Deny" "Permission requested" "Permission requested\nfor account %s." + "Permission requested by %1$s\nfor account %2$s." "You\'re using this app outside of your work profile" "You\'re using this app in your work profile" "Input Method" diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 596d4b658769..d1302d304a72 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1413,6 +1413,7 @@ "Deny" "Permission requested" "Permission requested\nfor account %s." + "Permission requested by %1$s\nfor account %2$s." "You\'re using this app outside of your work profile" "You\'re using this app in your work profile" "Input Method" diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 2cd891f57281..920808b9b492 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1413,6 +1413,7 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‎Deny‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎Permission requested‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎Permission requested‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎for account ‎‏‎‎‏‏‎%s‎‏‎‎‏‏‏‎.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‎Permission requested by ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎for account ‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎You\'re using this app outside of your work profile‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎You\'re using this app in your work profile‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎Input method‎‏‎‎‏‎" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 5b216612430e..8ad378d3ecd8 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1413,6 +1413,7 @@ "Denegar" "Permiso solicitado" "Permiso solicitado\npara la cuenta %s" + "%1$s solicitó permiso\npara acceder a la cuenta %2$s." "Estás utilizando esta aplicación fuera del perfil de trabajo." "Estás utilizando esta aplicación en tu perfil de trabajo." "Método de entrada" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 13f1b5613169..4f6dc6414a97 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1413,6 +1413,7 @@ "Denegar" "Permiso solicitado" "Permiso solicitado\npara la cuenta %s" + "Permiso solicitado por %1$s\npara acceder a la cuenta%2$s." "Estás usando esta aplicación fuera del perfil de trabajo" "Estás usando esta aplicación en tu perfil de trabajo" "Método de introducción de texto" diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index ea6fabe0556f..e58422ab4fee 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1413,6 +1413,7 @@ "Keela" "Taotletud luba" "Luba on taotletud\nkontole %s." + "Rakendus %1$s nõuab luba\nkontole %2$s juurdepääsemiseks." "Kasutate rakendust väljaspool tööprofiili" "Kasutate seda rakendust oma tööprofiilil" "Sisestusmeetod" diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 99012924a3e2..7bd4bfc56949 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1413,6 +1413,7 @@ "Ukatu" "Baimena eskatu da" "Baimena eskatu da \n%s konturako." + "%1$s aplikazioak %2$s kontua atzitzeko baimena\neskatu du." "Laneko profiletik kanpo ari zara aplikazioa erabiltzen" "Laneko profilean ari zara aplikazioa erabiltzen" "Idazketa-metodoa" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 3dd8173ae4e1..ff775bcab8eb 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1413,6 +1413,7 @@ "مجاز نبودن" "مجوز درخواست شد" "مجوز\nبرای حساب %s درخواست شد." + "%1$s برای دسترسی به حساب %2$s\nدرخواست اجازه کرد." "شما از این برنامه در خارج از نمایه کاری‌تان استفاده می‌کنید" "از این برنامه در نمایه کاری‌تان استفاده می‌کنید" "روش ورودی" diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 527d5a6a8087..87cc6af3b34c 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1413,6 +1413,7 @@ "Kiellä" "Lupa pyydetty" "Pyydetään lupaa\ntilille %s." + "%1$s pyytänyt pääsyä\ntilille %2$s." "Käytät sovellusta muulla kuin työprofiililla" "Käytät sovellusta työprofiililla" "Syöttötapa" diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 8002080a1005..f46e648232e2 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1413,6 +1413,7 @@ "Refuser" "Autorisation demandée" "Autorisation demandée\npour le compte \"%s\"" + "Autorisation demandée par %1$s\npour le compte %2$s." "Vous utilisez cette application en dehors de votre profil professionnel" "Vous utilisez cette application dans votre profil professionnel" "Mode de saisie" diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 7d777542e6f4..6355e621e8e9 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1413,6 +1413,7 @@ "Refuser" "Autorisation demandée" "Autorisation demandée\npour le compte \"%s\"" + "Autorisation demandée par %1$s\npour le compte %2$s." "Vous utilisez cette application en dehors de votre profil professionnel." "Vous utilisez cette application dans votre profil professionnel." "Mode de saisie" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 7cc6f6934901..86915dd2bb09 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1413,6 +1413,7 @@ "Rexeitar" "Permiso solicitado" "Permiso solicitado\npara a conta %s." + "%1$s solicitou permiso\npara acceder á conta %2$s." "Estás usando esta aplicación fóra do teu perfil de traballo" "Estás usando esta aplicación no teu perfil de traballo" "Método de introdución de texto" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 758b2a014633..d1bea9bc507b 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1413,6 +1413,7 @@ "નકારો" "પરવાનગીની વિનંતી કરી" "એકાઉન્ટ %s માટે\nપરવાનગીની વિનંતી કરી." + "%2$s એકાઉન્ટ માટે\n%1$s દ્વારા પરવાનગીની વિનંતી કરવામાં આવી." "તમે તમારી કાર્ય પ્રોફાઇલની બહાર આ એપ્લિકેશનનો ઉપયોગ કરી રહ્યાં છો" "તમે તમારી કાર્ય પ્રોફાઇલમાં આ એપ્લિકેશનનો ઉપયોગ કરી રહ્યાં છો" "ઇનપુટ પદ્ધતિ" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 7fdbb8a60989..481a9deb9ed1 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1413,6 +1413,7 @@ "अस्वीकारें" "अनुमति अनुरोधित" "%s खाते के लिए अनुमति\nका अनुरोध किया गया." + "%1$s ने %2$s खाते को ऐक्सेस\nकरने की अनुमति का अनुरोध किया है." "आप इस ऐप्स का उपयोग अपनी वर्क प्रोफ़ाइल से बाहर कर रहे हैं" "आप इस ऐप्स का उपयोग अपनी वर्क प्रोफ़ाइल में कर रहे हैं" "इनपुट विधि" diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 5d4905fb5680..18327375a156 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1433,6 +1433,7 @@ "Odbij" "Zatražena je dozvola" "Zatražena je dozvola\nza račun %s." + "Aplikacija %1$s zatražila je dopuštenje\nza račun %2$s." "Ovu aplikaciju upotrebljavate izvan svog radnog profila" "Upotrebljavate tu aplikaciju u radnom profilu" "Način unosa" diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 4492eec7d8be..f9c55767e1ca 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1413,6 +1413,7 @@ "Elutasítás" "Az engedélykérés megtörtént" "Az engedélykérés megtörtént\na(z) %s fiók számára." + "A(z) %1$s alkalmazás által kért engedély\na következő fiók számára: %2$s." "Ezt az alkalmazást munkaprofilján kívül használja" "Munkaprofiljában már használja az alkalmazást" "Beviteli mód" diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 2288e68d7b1b..986c3e7259ad 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1413,6 +1413,7 @@ "Մերժել" "Թույլտվության հարցում է արված" "Թույլտվության հարցում է արված\n%s հաշվի համար:" + "Հայցվում է թույլտվություն %1$s հավելվածի կողմից\n%2$s հաշվի համար" "Դուք օգտագործում եք այս հավելվածը ձեր աշխատանքային պրոֆիլից դուրս" "Դուք օգտագործում եք այս հավելվածը ձեր աշխատանքային պրոֆիլում" "Ներածման եղանակը" diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 12d479d95e8e..43238dc2c92b 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1413,6 +1413,7 @@ "Tolak" "Izin dimintakan" "Izin dimintakan\nuntuk akun %s." + "%1$s meminta izin\nuntuk akun %2$s." "Anda menggunakan aplikasi ini di luar profil kerja" "Anda menggunakan aplikasi ini di profil kerja" "Metode masukan" diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 1ca4e619b66c..dde1424f3379 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1413,6 +1413,7 @@ "Hafna" "Beðið um heimild" "Beðið um heimild\nfyrir reikninginn %s." + "Beiðni um heimild frá %1$s\nfyrir reikninginn %2$s." "Þú ert að nota þetta forrit utan vinnusniðsins" "Þú ert að nota þetta forrit á vinnusniðinu þínu" "Innsláttaraðferð" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 054344ebabe5..83a7ee88d2ac 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1413,6 +1413,7 @@ "Rifiuta" "Autorizzazione richiesta" "Autorizzazione richiesta\nper l\'account %s." + "Autorizzazione richiesta da %1$s\nper l\'account %2$s." "Stai utilizzando l\'app al di fuori del tuo profilo di lavoro" "Stai utilizzando l\'app nel tuo profilo di lavoro" "Metodo inserimento" diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 757041c4d397..c2f78a4921d6 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1453,6 +1453,7 @@ "עדיף שלא" "בקשת הרשאה" "נדרשת הרשאה\nלחשבון %s." + "התבקשה הרשאה על ידי %1$s\nלחשבון %2$s." "אתה משתמש באפליקציה זו מחוץ לפרופיל העבודה שלך" "אתה משתמש באפליקציה זו בפרופיל העבודה שלך" "שיטת קלט" diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 4a8179142c5b..526d9226d6ee 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1413,6 +1413,7 @@ "拒否" "権限がリクエストされました" "次のアカウントにアクセスする権限が\nリクエストされました: %s" + "アカウント %2$s へのアクセス権限が\n%1$s からリクエストされました。" "仕事用プロファイルの外部でこのアプリを使用しています" "仕事用プロファイルでこのアプリを使用しています" "入力方法" diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 7365b05ee513..8102f353a83d 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1413,6 +1413,7 @@ "აკრძალვა" "მოთხოვნილია ნებართვა" "მოთხოვნილია ნებრათვა \nანგარიშისთვის: %s" + "ნებართვა მოთხოვნილია %1$s-ის მიერ\nანგარიშისთვის %2$s." "იყენებთ ამ აპს თქვენს სამუშაო პროფილს მიღმა" "ამ აპს თქვენს სამუშაო პროფილში იყენებთ" "შეყვანის მეთოდი" diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 18476482fa42..e4c354a6b7b6 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1413,6 +1413,7 @@ "Бас тарту" "Рұқсат өтінілді" "Рұқсат \nесептік жазба үшін %s өтінілді." + "%2$s аккаунты үшін %1$s\nқолданбасы арқылы рұқсат сұралды." "Осы қолданбаны жұмыс профиліңізден тыс пайдаланып жатырсыз" "Осы қолданбаны жұмыс профиліңізде пайдаланып жатырсыз" "Енгізу әдісі" diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 95b253f25d78..692f8b0b4d3d 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1413,6 +1413,7 @@ "បដិសេធ" "បាន​ស្នើ​សិទ្ធិ" "បាន​ស្នើ​សិទ្ធិ\nសម្រាប់​គណនី %s ។" + "ការអនុញ្ញាត​ដែលស្នើដោយ %1$s\nសម្រាប់គណនី %2$s។" "អ្នក​កំពុង​ប្រើ​កម្មវិធី​នេះ​នៅ​ខាងក្រៅ​ប្រវត្តិរូប​​ការងារ​របស់​អ្នក" "អ្នក​កំពុង​ប្រើ​កម្មវិធី​នេះ​ក្នុង​ប្រវត្តិរូប​ការងារ​របស់​អ្នក" "វិធីសាស្ត្រ​បញ្ចូល" diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 37d20495cbde..019e6777d9fe 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1413,6 +1413,7 @@ "ನಿರಾಕರಿಸಿ" "ಅನುಮತಿ ವಿನಂತಿಸಲಾಗಿದೆ" "%s ಖಾತೆಗಾಗಿ\n ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ." + "%2$s ಖಾತೆಗಾಗಿ \n %1$s ನಿಂದ ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ." "ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ನ ಹೊರಗೆ ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ" "ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ನಲ್ಲಿ ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ" "ಇನ್‌ಪುಟ್ ವಿಧಾನ" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 7dcbaa00c476..6b390a0c6a78 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1413,6 +1413,7 @@ "거부" "권한 요청됨" "%s 계정에 대해\n권한 요청" + "%1$s에서 %2$s 계정에 대한\n권한을 요청했습니다" "직장 프로필 외부에서 이 앱을 사용 중입니다." "직장 프로필에서 이 앱을 사용 중입니다." "입력 방법" diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 6420e45fe02e..1eb107152bfe 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1413,6 +1413,7 @@ "Уруксат берилбейт" "Уруксат талап кылуу" "Кийинки эсепке\nуруксат талап кылынууда: %s." + "%1$s колдонмосу\n%2$s аккаунтуна кирүүгө уруксат сурады." "Бул колдонмо жумуш профилиңиздин сыртында колдонулуп жатат" "Бул колдонмону жумуш профилиңизде пайдаланып жатасыз" "Киргизүү ыкмасы" diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 47bd5a8da60e..57acf482dd1d 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1413,6 +1413,7 @@ "ປະ​ຕິ​ເສດ" "ຕ້ອງການການອະນຸຍາດ" "ຮ້ອງຂໍການກຳນົດສິດ\nສຳລັບບັນຊີ %s ແລ້ວ." + "ຮ້ອງຂໍການອະນຸຍາດໂດຍ %1$s\nສຳລັບບັນຊີ %2$s." "​ທ່ານ​ກຳ​ລັງ​ໃຊ້​ແອັບຯ​ນີ້ນອກ​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ​ຂອງ​ທ່ານ" "​ທ່ານ​ກຳ​ລັງ​ໃຊ້​ແອັບຯ​ນີ້​ໃນ​ໂປຣ​ໄຟລ໌​​ບ່ອນ​ເຮັດ​ວຽກ​ຂອງ​ທ່ານ" "ວິທີການປ້ອນຂໍ້ມູນ" diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 6a4958219732..cb69d1ab99d0 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1453,6 +1453,7 @@ "Atmesti" "Pateikta užklausa dėl leidimo" "Pateikta leidimo užklausa\ndėl %s paskyros" + "Programai „%1$s“ reikalingas leidimas\n, susijęs su paskyra %2$s." "Šią programą naudojate ne darbo profilyje" "Šią programą naudojate darbo profilyje" "Įvesties būdas" diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 8e67568e1718..d5caa65e5187 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1433,6 +1433,7 @@ "Noraidīt" "Atļauja ir pieprasīta." "Atļauja kontam %s\nir pieprasīta." + "Lietotne %1$s pieprasīja atļauju piekļūt \nkontam %2$s." "Šo lietotni izmantojat ārpus sava darba profila" "Jūs izmantojat šo lietotni no sava darba profila." "Ievades metode" diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 92d8e36fbe6b..32ba34c6a89d 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1413,6 +1413,7 @@ "Одбиј" "Побарана е дозвола" "Побарана е дозвола\nза сметка %s." + "%1$s побара дозвола\nза сметката %2$s." "Ја користите апликацијата надвор од работниот профил" "Ја користите апликацијата во работниот профил" "Метод на внес" diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 4a62f7286e63..ab631e9a5f4f 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1413,6 +1413,7 @@ "നിരസിക്കുക" "അനുമതി ആവശ്യമാണ്" "%s എന്ന അക്കൗണ്ടിനായി\nഅനുമതി അഭ്യർത്ഥിച്ചു." + "%2$s അക്കൗണ്ട് ആക്സസ് ചെയ്യാൻ \n%1$s അനുമതി അഭ്യർത്ഥിച്ചു." "നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിന് പുറത്ത് ഈ അപ്ലിക്കേഷൻ ഉപയോഗിക്കുന്നു" "നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിൽ ഈ അപ്ലിക്കേഷൻ ഉപയോഗിക്കുന്നു" "ടൈപ്പുചെയ്യൽ രീതി" diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index ed77e86a2ad1..768b8cd3a30f 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1413,6 +1413,7 @@ "Татгалзах" "Зөвшөөрөл хүсэв" "%s бүртгэл зөвшөөрөл \n хүссэн" + "%1$s\n нь %2$s бүртгэлд зөвшөөрөл хүссэн." "Та энэ апп-г өөрийн ажлын профайлаас гадуур ашиглаж байна" "Та энэ апп-г өөрийн ажлын профайл дотор ашиглаж байна" "Оруулах арга" diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 46057eea940e..80c490b78712 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1413,6 +1413,7 @@ "नकार द्या" "परवानगीची विनंती केली" "%s खात्यासाठी\nपरवानगीची विनंती केली." + "%1$s ने तुमचे \n%2$s खाते ॲक्सेस करण्यासाठी परवानगीची विनंती केली आहे." "तुम्ही हा अ‍ॅप आपल्‍या कार्य प्रोफाईलच्या बाहेर वापरत आहात" "तुम्ही हा अ‍ॅप आपल्या कार्य प्रोफाईलमध्‍ये वापरत आहात" "इनपुट पद्धत" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index f8efcfd5216a..cf91addc0b9d 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1413,6 +1413,7 @@ "Nafi" "Kebenaran diminta" "Kebenaran diminta\nuntuk akaun %s." + "Kebenaran diminta oleh %1$s\nuntuk akaun %2$s." "Anda menggunakan apl ini di luar profil kerja anda" "Anda menggunakan apl ini dalam profil kerja anda" "Kaedah input" diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index c0e3fb9a30fb..675c1837dd75 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1413,6 +1413,7 @@ "ငြင်းပယ်ရန်" "ခွင့်ပြုချက် တောင်းခံထားခြင်း" "အကောင့် %s အတွက် \n ခွင့်ပြုချက် တောင်းခံထားပြီး" + "%2$s အကောင့်အတွက်\n%1$s က ခွင့်ပြုချက် တောင်းခံထားသည်။" "သင်သည် ဒီအက်ပ်ကို သင့်အလုပ်ပရိုဖိုင် ပြင်ပတွင် အသုံးပြုနေ၏" "သင်သည် ဒီအက်ပ်ကို သင်၏ အလုပ် ပရိုဖိုင် ထဲမှာ အသုံးပြုနေသည်" "ထည့်သွင်းရန်နည်းလမ်း" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 4a89f37af147..7b99ec98ec51 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1413,6 +1413,7 @@ "Avslå" "Tillatelse forespurt" "Tillatelse forespurt\nfor kontoen %s." + "Tillatelse forespurt av %1$s\nfor kontoen %2$s." "Du bruker denne appen utenfor arbeidsprofilen" "Du bruker denne appen i jobbprofilen din" "Inndatametode" diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 343d0b22e4d4..ba55cc3b7521 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1413,6 +1413,7 @@ "अस्वीकार गर्नुहोस्" "अनुरोध गरिएको अनुमति" \n"खाता %sको लागि अनुरोध गरिएको अनुमति।" + "%1$s ले %2$s खाता चलाउने\nअनुमति मागेको छ।" "तपाईं तपाईंको कार्य प्रोफाइल बाहिर यो एप प्रयोग गरिरहनु भएको छ" "तपाईं आफ्नो कार्य प्रोफाइलमा यो एप प्रयोग गरिरहनु भएको छ" "इनपुट विधि" diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 1a8ce28e3561..0937031d99bd 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1411,8 +1411,8 @@ "Verzoek om toegang" "Toestaan" "Weigeren" - "Rechten gevraagd" - "Rechten gevraagd\nvoor account %s." + "Toestemming gevraagd" + "Toestemming gevraagd\nvoor account %s." "Je gebruikt deze app buiten je werkprofiel" "U gebruikt deze app in je werkprofiel" "Invoermethode" diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 386d9e05124d..9af7cf3a69be 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1413,6 +1413,7 @@ "ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ" "ଅନୁମତି ଅନୁରୋଧ କରାଯାଇଛି" "%s ଆକାଉଣ୍ଟ ପାଇଁ ଅନୁମତି\n ଅନୁରୋଧ କରାଯାଇଛି।" + "%2$s ଆକାଉଣ୍ଟକୁ ଆକ୍ସେସ୍ ପାଇଁ\n%1$s ଦ୍ୱାରା ଅନୁମତି ନିମନ୍ତେ ଅନୁରୋଧ କରାଯାଇଛି।" "ଆପଣ ନିଜର ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ ବାହାରେ ଏହି ଆପ୍‌ର ପ୍ରୟୋଗ କରୁଛନ୍ତି" "ଆପଣ ନିଜ ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ରେ ଏହି ଆପ୍‌ର ବ୍ୟବହାର କରୁଛନ୍ତି" "ଇନପୁଟ୍ ପଦ୍ଧତି" diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 77aceb7264f4..caded7e6a008 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1413,6 +1413,7 @@ "ਅਸਵੀਕਾਰ ਕਰੋ" "ਅਨੁਮਤੀ ਦੀ ਬੇਨਤੀ ਕੀਤੀ" "%s ਖਾਤੇ ਲਈ ਅਨੁਮਤੀ ਦੀ ਬੇਨਤੀ ਕੀਤੀ\n।" + "%1$s ਨੇ %2$s ਖਾਤੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ\nਦੀ ਇਜਾਜ਼ਤ ਲਈ ਬੇਨਤੀ ਕੀਤੀ।" "ਤੁਸੀਂ ਇਹ ਐਪ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਦੇ ਬਾਹਰ ਵਰਤ ਰਹੇ ਹੋ" "ਤੁਸੀਂ ਇਹ ਐਪ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਵਰਤ ਰਹੇ ਹੋ" "ਇਨਪੁੱਟ ਵਿਧੀ" diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 9b6edd21b6ac..1a84dba1e880 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1453,8 +1453,8 @@ "Odmów" "Prośba o pozwolenie" "Prośba o pozwolenie\ndotyczące konta %s" - "Używasz tej aplikacji poza profilem służbowym" - "Używasz tej aplikacji w swoim profilu służbowym" + "Używasz tej aplikacji poza profilem do pracy" + "Używasz tej aplikacji w swoim profilu do pracy" "Sposób wprowadzania tekstu" "Synchronizacja" "Ułatwienia dostępu" diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index ec34e4e6f8c8..35ccbdb06e5a 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1413,6 +1413,7 @@ "Negar" "Permissão solicitada" "Permissão solicitada\npara a conta %s." + "Permissão solicitada pelo app %1$s\npara a conta %2$s." "Este app está sendo usado fora de seu perfil de trabalho" "Você está usando este app em seu perfil de trabalho" "Método de entrada" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 6de3cdffefae..387d9a79db94 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1413,6 +1413,7 @@ "Recusar" "Permissão solicitada" "Permissão solicitada\npara a conta %s." + "Autorização solicitada pela app %1$s\npara a conta %2$s." "Está a utilizar esta app fora do seu perfil de trabalho" "Está a utilizar esta app no seu perfil de trabalho" "Método de entrada" diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index ec34e4e6f8c8..35ccbdb06e5a 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1413,6 +1413,7 @@ "Negar" "Permissão solicitada" "Permissão solicitada\npara a conta %s." + "Permissão solicitada pelo app %1$s\npara a conta %2$s." "Este app está sendo usado fora de seu perfil de trabalho" "Você está usando este app em seu perfil de trabalho" "Método de entrada" diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index bffa4570ef2a..b77851b76cf6 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1433,6 +1433,7 @@ "Refuzați" "Permisiune solicitată" "Permisiune solicitată\npentru contul %s." + "Permisiune solicitată de %1$s\npentru contul %2$s." "Utilizați această aplicație în afara profilului de serviciu" "Utilizați această aplicație în profilul de serviciu" "Metodă de intrare" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 20407b131907..2fab14de573d 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1453,6 +1453,7 @@ "Отклонить" "Разрешение запрошено" "Требуется разрешение\nдля аккаунта %s." + "Разрешение запрошено приложением \"%1$s\"\nдля аккаунта %2$s." "Это приложение используется в личном профиле" "Вы перешли в рабочий профиль" "Способ ввода" diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index dc8f723af411..825fdfa3af0b 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1413,6 +1413,7 @@ "ප්‍රතික්ෂේප කරන්න" "අවසර ඉල්ලා සිටී" "%s ගිණුම සඳහා\nඅවසර ඉල්ලන ලදි." + "%2$s ගිණුම සඳහා %1$s\n විසින් ඉල්ලූ අවසරය" "මෙම යෙදුම ඔබගේ කාර්යාල පැතිකඩින් පිට දී ඔබ භාවිතා කරයි" "මෙම යෙදුම ඔබගේ පුද්ගලික කොටසේ ඔබ භාවිතා කරයි" "ආදාන ක්‍රමය" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 624d55492603..cfc5f394150e 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1453,6 +1453,7 @@ "Zamietnuť" "Vyžaduje sa povolenie" "Vyžaduje sa oprávnenie\npre účet %s" + "Povolenia, ktoré aplikácia %1$s požaduje\npre účet %2$s." "Túto aplikáciu používate mimo svojho pracovného profilu" "Túto aplikáciu používate vo svojom pracovnom profile" "Metóda vstupu" diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 32cfb18501d2..0c681ec9a49f 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1453,6 +1453,7 @@ "Zavrni" "Zahtevano je dovoljenje" "Zahtevano je dovoljenje\nza račun %s." + "Aplikacija %1$s je zahtevala dovoljenje\nza račun %2$s." "Aplikacijo uporabljate zunaj delovnega profila" "To aplikacijo uporabljate v delovnem profilu" "Način vnosa" diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 0c2b143bc808..6e0436ed8884 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1413,6 +1413,7 @@ "Moho" "Kërkohet leje" "Kërkohet leje\npër llogarinë %s." + "Kërkohet leja nga %1$s\npër llogarinë %2$s." "Po e përdor këtë aplikacion jashtë profilit tënd të punës" "Këtë aplikacion po e përdor në profilin tënd të punës" "Metoda e hyrjeve" diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index d2e78651ef22..d69ed48f09e5 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1433,6 +1433,7 @@ "Одбиј" "Затражена је дозвола" "Затражена је дозвола\nза налог %s" + "%1$s тражи дозволу \nза налог %2$s." "Користите ову апликацију изван пословног профила" "Користите ову апликацију на пословном профилу" "Метод уноса" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 66bf9c2d8030..6688fe210b9b 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1413,6 +1413,7 @@ "Neka" "Begärd behörighet" "Begärd behörighet\nför kontot %s." + "Behörighet har begärts av %1$s\nför kontot %2$s." "Du använder den här appen i din jobbprofil" "Du använder den här appen i din jobbprofil" "Indatametod" diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 8aca7d259c30..ef35a6fe4420 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1413,6 +1413,7 @@ "Kataza" "Idhini imeitishwa" "Idhini imeombwa\nya akaunti%s." + "%1$s imeombwa ruhusa\nkwenye akaunti ya %2$s." "Unatumia programu hii nje ya wasifu wako wa kazini" "Unatumia programu hii kwenye wasifu wako wa kazini" "Mbinu ya uingizaji" diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 2eb628c7afb0..d6410beb93cd 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1413,6 +1413,7 @@ "நிராகரி" "அனுமதிக் கோரப்பட்டது" "%s கணக்கிற்கான அனுமதி\nகோரப்பட்டது." + "%1$s ஆப்ஸ்\n%2$s கணக்கிற்கான அனுமதியைக் கோருகிறது." "இந்தப் பயன்பாட்டைப் பணிக் கணக்கிற்கு வெளியே பயன்படுத்துகிறீர்கள்" "பணிக் கணக்கில் பயன்பாட்டைப் பயன்படுத்துகிறீர்கள்" "உள்ளீட்டு முறை" diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index f379214734c9..988169835979 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1413,8 +1413,8 @@ "తిరస్కరించండి" "అనుమతి అభ్యర్థించబడింది" "ఖాతా %s కోసం\nఅనుమతి అభ్యర్థించబడింది." - "మీరు మీ కార్యాలయ ప్రొఫైల్‌కు వెలుపల ఈ యాప్‌ను ఉపయోగిస్తున్నారు" - "మీరు మీ కార్యాలయ ప్రొఫైల్‌లో ఈ యాప్‌ను ఉపయోగిస్తున్నారు" + "మీరు మీ కార్యాలయ ప్రొఫైల్‌కు వెలుపల ఈ అనువర్తనాన్ని ఉపయోగిస్తున్నారు" + "మీరు మీ కార్యాలయ ప్రొఫైల్‌లో ఈ అనువర్తనాన్ని ఉపయోగిస్తున్నారు" "ఇన్‌పుట్ పద్ధతి" "సమకాలీకరణ" "యాక్సెస్ సామర్థ్యం" diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 4c6be820d4ff..08718ffd614f 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1413,6 +1413,7 @@ "ปฏิเสธ" "การอนุญาตที่ขอ" "การอนุญาตที่ขอ\nสำหรับบัญชี %s" + "สิทธิ์ที่ %1$s ขอ\nสำหรับบัญชี %2$s" "คุณกำลังใช้แอปนี้นอกโปรไฟล์งานของคุณ" "คุณกำลังใช้แอปนี้ในโปรไฟล์งานของคุณ" "วิธีป้อนข้อมูล" diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 0bd750f1aa33..0a7aa0e7ab14 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1413,6 +1413,7 @@ "Tanggihan" "Hiniling ang pahintulot" "Hiniling ang pahintulot\npara sa account na %s." + "Humiling ang %1$s ng pahintulot\npara sa account na %2$s." "Ginagamit mo ang app na ito sa labas ng iyong profile sa trabaho" "Ginagamit mo ang app na ito sa iyong profile sa trabaho" "Pamamaraan ng pag-input" diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 87d97599a9bb..a44882498142 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1413,6 +1413,7 @@ "Reddet" "İzin istendi" "%s hesabı için\nizin isteğinde bulunuldu." + "%1$s uygulaması, %2$s hesabı\niçin izin istedi" "Bu uygulamayı iş profilinizin dışında kullanıyorsunuz" "Bu uygulamayı iş profilinizde kullanıyorsunuz" "Giriş yöntemi" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 067045aa0860..4605db387a44 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1453,6 +1453,7 @@ "Забор." "Потрібен дозвіл" "Запитано дозвіл\nдля облікового запису %s." + "Додаток %1$s запитує дозвіл\nна доступ до облікового запису %2$s." "Ви використовуєте цей додаток за межами робочого профілю" "Ви використовуєте цей додаток у своєму робочому профілі" "Метод введення" diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 04800531784e..06dc0751e76e 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1413,6 +1413,7 @@ "مسترد کریں" "اجازت طلب کی گئی" "اکاؤنٹ %s کیلئے\nاجازت طلب کی گئی۔" + "%2$s اکاؤنٹ کیلئے\n%1$s نے اجازت کی درخواست کی۔" "آپ اس ایپ کا استعمال اپنے دفتری پروفائل کے باہر کر رہے ہیں" "آپ اس ایپ کو اپنے دفتری پروفائل میں استعمال کر رہے ہیں" "اندراج کا طریقہ" diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index e3ed7829f1ec..f09aaa67c43a 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1413,6 +1413,7 @@ "Rad etish" "Ruxsat so‘raldi" "%s hisobi uchun\nruxsat so‘raldi" + "Ruxsat %1$s ilovasi tomonidan \n%2$s hisobi uchun soʻralgan." "Siz ushbu ilovadan ishchi profilingizdan tashqarida foydalanmoqdasiz" "Siz ushbu ilovadan ishchi profilingizda foydalanmoqdasiz" "Kiritish uslubi" diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 8e0cc2ead613..d19ecf567c54 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1413,6 +1413,7 @@ "Từ chối" "Đã yêu cầu quyền" "Đã yêu cầu quyền\ncho tài khoản %s." + "Quyền do %1$s\nyêu cầu cho tài khoản %2$s." "Bạn đang sử dụng ứng dụng này bên ngoài hồ sơ công việc của mình" "Bạn đang sử dụng ứng dụng này trong hồ sơ công việc của mình" "Phương thức nhập" diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index a645cd7db90a..74dde0ec4550 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1413,6 +1413,7 @@ "拒绝" "权限请求" "应用对帐号 %s\n 提出权限请求。" + "“%1$s”请求获得以下帐号的访问权限:\n%2$s。" "您目前是在工作资料之外使用此应用" "您目前是在工作资料内使用此应用" "输入法" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 9eef703670ef..6cae6439ffd3 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1413,6 +1413,7 @@ "拒絕" "已要求權限" "%s 帳戶的\n權限要求。" + "「%1$s」要求帳戶 %2$s\n的權限" "您目前並未透過公司檔案使用這個應用程式" "您目前透過公司檔案使用這個應用程式" "輸入法" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index c4f5dc8d3661..58af46e65f60 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1413,6 +1413,7 @@ "拒絕" "已要求權限" "帳戶 %s 已提出\n權限要求。" + "「%1$s」要求授予\n%2$s 帳戶的權限。" "你目前並非透過工作資料夾使用這個應用程式" "你目前透過工作設定檔使用這個應用程式" "輸入法" diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index bb5360edb0b8..c84b44720467 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1413,6 +1413,7 @@ "Yala" "Imvume Iceliwe" "Imvume Iceliwe \n ye-akhawunti %s" + "Imvume ecelwe yi-%1$s\nye-akhawunti ye-%2$s." "Usebenzisa lolu hlelo lokusebenza ngaphandle kwephrofayela yakho yomsebenzi" "Usebenzisa lolu hlelo lokusebenza kuphrofayela yakho yomsebenzi" "Indlela yokufakwayo" From 3a8d46c25adebc9c0a63b6b85a427ed21427f098 Mon Sep 17 00:00:00 2001 From: Aseem Kumar Date: Mon, 17 May 2021 09:25:03 +0000 Subject: [PATCH 015/208] Prevent apps from spamming addAccountExplicitly. See comment here for the discussion on solution https://b.corp.google.com/issues/169762606#comment14 Change-Id: If212df3a3b7be1de0fb26b8e88b2fcbb8077c253 Bug: 169762606 (cherry picked from commit 11053c17b397db67b20e96ce769508766cef7db9) Change-Id: I3ff7d8f4df086cb4c153e7ec873b85a093810722 Merged-In: If212df3a3b7be1de0fb26b8e88b2fcbb8077c253 (cherry picked from commit c65b81ba2728bfc3d296b8b3fbe0acacd67d1bd6) Merged-In:I3ff7d8f4df086cb4c153e7ec873b85a093810722 --- core/java/android/accounts/Account.java | 7 +++++++ .../com/android/server/accounts/AccountManagerService.java | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 9a18880a353b..965b6e0a02cd 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -30,6 +30,7 @@ import com.android.internal.annotations.GuardedBy; +import java.util.Objects; import java.util.Set; /** @@ -85,6 +86,12 @@ public Account(String name, String type, String accessId) { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("the type must not be empty: " + type); } + if (name.length() > 200) { + throw new IllegalArgumentException("account name is longer than 200 characters"); + } + if (type.length() > 200) { + throw new IllegalArgumentException("account type is longer than 200 characters"); + } this.name = name; this.type = type; this.accessId = accessId; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index db3c25a7e43a..90dc03642a86 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1820,6 +1820,11 @@ private boolean addAccountInternal(UserAccounts accounts, Account account, Strin + ", skipping since the account already exists"); return false; } + if (accounts.accountsDb.findAllDeAccounts().size() > 100) { + Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() + + ", skipping since more than 50 accounts on device exist"); + return false; + } long accountId = accounts.accountsDb.insertCeAccount(account, password); if (accountId < 0) { Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() From 131d972a82d635c6fe601713594692a412e4e894 Mon Sep 17 00:00:00 2001 From: Jeff DeCew Date: Thu, 21 Oct 2021 19:48:19 +0000 Subject: [PATCH 016/208] Allow forcing status bar state changes and do so when the screen turns off. This adds a force flag, which we will use when turning the screen off to make sure that all UI components are reset to the SHADE state regardless. Bug: 189575031 Test: make a call; lock screen; pull down shade Merged-In: I79baeb71ac5d1ed45602ac55cdca996b3bed0ac3 Change-Id: I79baeb71ac5d1ed45602ac55cdca996b3bed0ac3 (cherry picked from commit 9be6207510c2e39e2899a9ce7a93fb09f83134c6) Merged-In:I79baeb71ac5d1ed45602ac55cdca996b3bed0ac3 --- .../statusbar/StatusBarStateControllerImpl.java | 4 ++-- .../statusbar/SysuiStatusBarStateController.java | 14 +++++++++++++- .../systemui/statusbar/phone/StatusBar.java | 13 +++++++++---- .../systemui/statusbar/phone/StatusBarTest.java | 6 ++++-- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 2bef355d59f3..e0e603f7514d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -134,11 +134,11 @@ public int getState() { } @Override - public boolean setState(int state) { + public boolean setState(int state, boolean force) { if (state > MAX_STATE || state < MIN_STATE) { throw new IllegalArgumentException("Invalid state " + state); } - if (state == mState) { + if (!force && state == mState) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 07b35502478f..fc40077810ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -58,7 +58,19 @@ public interface SysuiStatusBarStateController extends StatusBarStateController * @param state see {@link StatusBarState} for valid options * @return {@code true} if the state changed, else {@code false} */ - boolean setState(int state); + default boolean setState(int state) { + return setState(state, false /* force */); + } + + /** + * Update the status bar state + * @param state see {@link StatusBarState} for valid options + * @param force whether to set the state even if it's the same as the current state. This will + * dispatch the state to all StatusBarStateListeners, ensuring that all listening + * components are reset to this state. + * @return {@code true} if the state was changed or set forcefully + */ + boolean setState(int state, boolean force); /** * Update the dozing state from {@link StatusBar}'s perspective diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 55a537a2e578..e230300e714c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3467,6 +3467,10 @@ public boolean isFullScreenUserSwitcherState() { } boolean updateIsKeyguard() { + return updateIsKeyguard(false /* force */); + } + + boolean updateIsKeyguard(boolean force) { boolean wakeAndUnlocking = mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -3489,7 +3493,7 @@ boolean updateIsKeyguard() { showKeyguardImpl(); } } else { - return hideKeyguardImpl(); + return hideKeyguardImpl(force); } return false; } @@ -3620,11 +3624,11 @@ private void runLaunchTransitionEndRunnable() { /** * @return true if we would like to stay in the shade, false if it should go away entirely */ - public boolean hideKeyguardImpl() { + public boolean hideKeyguardImpl(boolean force) { mIsKeyguard = false; Trace.beginSection("StatusBar#hideKeyguard"); boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide(); - if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) { + if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) { //TODO: StatusBarStateController should probably know about hiding the keyguard and // notify listeners. @@ -4056,7 +4060,8 @@ public void onFinishedGoingToSleep() { // is correct. mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource)); } - updateIsKeyguard(); + // When finished going to sleep, force the status bar state to avoid stale state. + updateIsKeyguard(true /* force */); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index c6dba6b8b800..2cc4ceea3f94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -815,12 +815,14 @@ public void testShowKeyguardImplementation_setsState() { // By default, showKeyguardImpl sets state to KEYGUARD. mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD)); + verify(mStatusBarStateController).setState( + eq(StatusBarState.KEYGUARD), eq(false) /* force */); // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER. when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true); mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER)); + verify(mStatusBarStateController).setState( + eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */); } @Test From d449e440e8e5acb089f524a0ef55a45d770cefcf Mon Sep 17 00:00:00 2001 From: Varun Shah Date: Mon, 8 Nov 2021 17:27:32 -0800 Subject: [PATCH 017/208] Update deletion conditions for a package's UsageStats. If a profile owner is defined for a specific user, do not delete usage stats for a package on package deletion. Bug: 197399948 Test: atest UsageStatsTest [all] Change-Id: I94a8e3dfca8ef4c7616f77944d61726e06043b85 Merged-In: I94a8e3dfca8ef4c7616f77944d61726e06043b85 (cherry picked from commit d95ce6779da8410c5835385cb5785fb5b3a51d83) Merged-In:I94a8e3dfca8ef4c7616f77944d61726e06043b85 --- .../admin/DevicePolicyManagerInternal.java | 8 ++++++ .../DevicePolicyManagerService.java | 7 +++++ .../server/usage/UsageStatsService.java | 26 +++++++++++++++---- .../server/usage/UserUsageStatsService.java | 10 ++++--- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index d24694faff93..60cb563cca3d 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -16,6 +16,7 @@ package android.app.admin; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; @@ -76,6 +77,13 @@ public interface OnCrossProfileWidgetProvidersChangeListener { public abstract void addOnCrossProfileWidgetProvidersChangeListener( OnCrossProfileWidgetProvidersChangeListener listener); + /** + * @param userHandle the handle of the user whose profile owner is being fetched. + * @return the configured supervision app if it exists and is the device owner or policy owner. + */ + public abstract @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent( + @NonNull UserHandle userHandle); + /** * Checks if an app with given uid is an active device admin of its user and has the policy * specified. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e911ada1026a..13fc10cc2707 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12509,6 +12509,13 @@ public void addOnCrossProfileWidgetProvidersChangeListener( } } + @Override + public @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent( + @NonNull UserHandle userHandle) { + return DevicePolicyManagerService.this.getProfileOwnerOrDeviceOwnerSupervisionComponent( + userHandle); + } + @Override public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) { synchronized (getLockObject()) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a5b676012ea9..a54f263c32ae 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -339,6 +339,7 @@ private void onUserUnlocked(int userId) { if (userId == UserHandle.USER_SYSTEM) { UsageStatsIdleService.scheduleUpdateMappingsJob(getContext()); } + final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId)); synchronized (mLock) { // Create a user unlocked event to report final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime()); @@ -356,7 +357,7 @@ private void onUserUnlocked(int userId) { boolean needToFlush = !pendingEvents.isEmpty(); initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(), - installedPackages); + installedPackages, deleteObsoleteData); mUserUnlockedStates.put(userId, true); final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId); if (userService == null) { @@ -551,13 +552,13 @@ private UserUsageStatsService getUserUsageStatsServiceLocked(int userId) { * when the user is initially unlocked. */ private void initializeUserUsageStatsServiceLocked(int userId, long currentTimeMillis, - HashMap installedPackages) { + HashMap installedPackages, boolean deleteObsoleteData) { final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId), "usagestats"); final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId, usageStatsDir, this); try { - service.init(currentTimeMillis, installedPackages); + service.init(currentTimeMillis, installedPackages, deleteObsoleteData); mUserState.put(userId, service); } catch (Exception e) { if (mUserManager.isUserUnlocked(userId)) { @@ -1029,6 +1030,10 @@ private boolean pruneUninstalledPackagesData(int userId) { * Called by the Binder stub. */ private boolean updatePackageMappingsData() { + // don't update the mappings if a profile user is defined + if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) { + return true; // return true so job scheduler doesn't reschedule the job + } // fetch the installed packages outside the lock so it doesn't block package manager. final HashMap installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM); synchronized (mLock) { @@ -1154,6 +1159,13 @@ UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime, } } + private boolean shouldDeleteObsoleteData(UserHandle userHandle) { + final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); + // If a profile owner is not defined for the given user, obsolete data should be deleted + return dpmInternal == null + || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null; + } + private String buildFullToken(String packageName, String token) { final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1); sb.append(packageName); @@ -2324,8 +2336,12 @@ public boolean updatePackageMappingsData() { private class MyPackageMonitor extends PackageMonitor { @Override public void onPackageRemoved(String packageName, int uid) { - mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName) - .sendToTarget(); + final int changingUserId = getChangingUserId(); + // Only remove the package's data if a profile owner is not defined for the user + if (shouldDeleteObsoleteData(UserHandle.of(changingUserId))) { + mHandler.obtainMessage(MSG_PACKAGE_REMOVED, changingUserId, 0, packageName) + .sendToTarget(); + } super.onPackageRemoved(packageName, uid); } } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 26de11af6f4e..0a5adc8e61d4 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -115,8 +115,9 @@ interface StatsUpdatedListener { mSystemTimeSnapshot = System.currentTimeMillis(); } - void init(final long currentTimeMillis, HashMap installedPackages) { - readPackageMappingsLocked(installedPackages); + void init(final long currentTimeMillis, HashMap installedPackages, + boolean deleteObsoleteData) { + readPackageMappingsLocked(installedPackages, deleteObsoleteData); mDatabase.init(currentTimeMillis); if (mDatabase.wasUpgradePerformed()) { mDatabase.prunePackagesDataOnUpgrade(installedPackages); @@ -180,12 +181,13 @@ int onPackageRemoved(String packageName, long timeRemoved) { return mDatabase.onPackageRemoved(packageName, timeRemoved); } - private void readPackageMappingsLocked(HashMap installedPackages) { + private void readPackageMappingsLocked(HashMap installedPackages, + boolean deleteObsoleteData) { mDatabase.readMappingsLocked(); // Package mappings for the system user are updated after 24 hours via a job scheduled by // UsageStatsIdleService to ensure restored data is not lost on first boot. Additionally, // this makes user service initialization a little quicker on subsequent boots. - if (mUserId != UserHandle.USER_SYSTEM) { + if (mUserId != UserHandle.USER_SYSTEM && deleteObsoleteData) { updatePackageMappingsLocked(installedPackages); } } From 72e95726439fdb99530188bd1f218250a9188914 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Thu, 14 Oct 2021 09:12:44 -0700 Subject: [PATCH 018/208] Revert "Revert "[pm] remove old stage dirs on low storage"" This reverts commit b45ebca772a77bf3fd269d0dcfc1a7af7c73861d. Reason for revert: adding the fix for system to abandon sessions BUG: 67862680 Test: manual Change-Id: I5b762a29d12f62b516a59a85530386236259308b Merged-In: I91170ba399b3a596320b3bd9c8188912e5c4f1be (cherry picked from commit c98f06c286eb0d41f57e0a00b9f59044b9f12706) (cherry picked from commit 8408d434030650c7ab26197030375765b24eeedd) Merged-In:I5b762a29d12f62b516a59a85530386236259308b --- .../server/pm/PackageInstallerService.java | 55 ++++++++++++++++--- .../server/pm/PackageInstallerSession.java | 8 +-- .../server/pm/PackageManagerService.java | 4 ++ 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index f8115d39b375..30fed643b3fa 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -134,6 +134,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** Upper bound on number of historical sessions for a UID */ private static final long MAX_HISTORICAL_SESSIONS = 1048576; + /** Destroy sessions older than this on storage free request */ + private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS; + /** * Allow verification-skipping if it's a development app installed through ADB with * disable verification flag specified. @@ -295,22 +298,28 @@ && getSession(session.getParentSessionId()) == null) { @GuardedBy("mSessions") private void reconcileStagesLocked(String volumeUuid) { - final File stagingDir = getTmpSessionDir(volumeUuid); - final ArraySet unclaimedStages = newArraySet( - stagingDir.listFiles(sStageFilter)); - - // We also need to clean up orphaned staging directory for staged sessions - final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); - unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles())); - + final ArraySet unclaimedStages = getStagingDirsOnVolume(volumeUuid); // Ignore stages claimed by active sessions for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); unclaimedStages.remove(session.stageDir); } + removeStagingDirs(unclaimedStages); + } + + private ArraySet getStagingDirsOnVolume(String volumeUuid) { + final File stagingDir = getTmpSessionDir(volumeUuid); + final ArraySet stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter)); + + // We also need to clean up orphaned staging directory for staged sessions + final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); + stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles())); + return stagingDirs; + } + private void removeStagingDirs(ArraySet stagingDirsToRemove) { // Clean up orphaned staging directories - for (File stage : unclaimedStages) { + for (File stage : stagingDirsToRemove) { Slog.w(TAG, "Deleting orphan stage " + stage); synchronized (mPm.mInstallLock) { mPm.removeCodePathLI(stage); @@ -318,6 +327,34 @@ private void reconcileStagesLocked(String volumeUuid) { } } + /** + * Called to free up some storage space from obsolete installation files + */ + public void freeStageDirs(String volumeUuid) { + final ArraySet unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid); + final long currentTimeMillis = System.currentTimeMillis(); + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) { + // Only handles sessions stored on the target volume + continue; + } + final long age = currentTimeMillis - session.createdMillis; + if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) { + // Aggressively close old sessions because we are running low on storage + // Their staging dirs will be removed too + session.abandon(); + } else { + // Session is new enough, so it deserves to be kept even on low storage + unclaimedStagingDirsOnVolume.remove(session.stageDir); + } + } + } + removeStagingDirs(unclaimedStagingDirsOnVolume); + } + + public void onPrivateVolumeMounted(String volumeUuid) { synchronized (mSessions) { reconcileStagesLocked(volumeUuid); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d690ae9e70f1..a253b509abb9 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2638,14 +2638,8 @@ public void abandon() { List childSessions = getChildSessionsNotLocked(); synchronized (mLock) { - if (params.isStaged && mDestroyed) { - // If a user abandons staged session in an unsafe state, then system will try to - // abandon the destroyed staged session when it is safe on behalf of the user. - assertCallerIsOwnerOrRootOrSystemLocked(); - } else { - assertCallerIsOwnerOrRootLocked(); - } + assertCallerIsOwnerOrRootOrSystemLocked(); if (isStagedAndInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be // removed by PackageInstallerService when the last update time is old enough. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0a5f961559a8..4b119598ad3a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5210,6 +5210,10 @@ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } + + // 12. Clear temp install session files + mInstallerService.freeStageDirs(volumeUuid); + } else { try { mInstaller.freeCache(volumeUuid, bytes, 0, 0); From 2dc509bbb2bdae56127b798f5f6e387602ee1d8b Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Mon, 15 Nov 2021 19:21:44 +0000 Subject: [PATCH 019/208] RESTRICT AUTOMERGE Remove line of code that was mistakently left in. This line was removed in O, S, & P, but somehow survived in the Q and R branches. Bug: 193444889 Merged-In: I56589865427b10e2eab68e1ed2e7c290572a9edc Change-Id: I56589865427b10e2eab68e1ed2e7c290572a9edc (cherry picked from commit 1b13bc873c7682847cdfa904f754ebc327eb180b) Merged-In:I56589865427b10e2eab68e1ed2e7c290572a9edc --- .../com/android/systemui/shared/plugins/PluginManagerImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index a687bb89fd75..5c742f25209e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -201,7 +201,6 @@ private void startListening() { filter.addAction(DISABLE_PLUGIN); filter.addDataScheme("package"); mContext.registerReceiver(this, filter, PluginInstanceManager.PLUGIN_PERMISSION, null); - mContext.registerReceiver(this, filter); filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiver(this, filter); } From b871bc0cfb7fcc46f78c4d3d8aa4b7b807cd4ee7 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Mon, 4 Oct 2021 15:33:14 -0400 Subject: [PATCH 020/208] [DO NOT MERGE] Controls - Do not recreate intent Recreating the control's intent in SystemUI can be exploited to launch Intent's with SystemUI's privileges, rather than what is limited to the application. Use the fillInIntent parameter to supply additional parameters to the application. Bug: 193445603 Test: Follow directions in bug to retest Change-Id: Ib2b0342af85679c0514fb4d88530376b58e6e12a Merged-In: (cherry picked from commit 0e120a5d4f8998e343f3c8106081037017613082) (cherry picked from commit d7b16dd2a56035b2feec9f39d6d461fd551317eb) Merged-In:Ib2b0342af85679c0514fb4d88530376b58e6e12a --- .../ui/ControlActionCoordinatorImpl.kt | 13 ++-- .../systemui/controls/ui/DetailDialog.kt | 32 +++++--- .../systemui/controls/ui/DetailDialogTest.kt | 74 +++++++++++++++++++ 3 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index e15380b42a78..f6b420ba57f3 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.ui import android.app.Dialog +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -74,7 +75,7 @@ class ControlActionCoordinatorImpl @Inject constructor( bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) if (cvh.usePanel()) { - showDialog(cvh, control.getAppIntent().getIntent()) + showDetail(cvh, control.getAppIntent()) } else { cvh.action(CommandAction(templateId)) } @@ -100,7 +101,7 @@ class ControlActionCoordinatorImpl @Inject constructor( // Long press snould only be called when there is valid control state, otherwise ignore cvh.cws.control?.let { cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) - showDialog(cvh, it.getAppIntent().getIntent()) + showDetail(cvh, it.getAppIntent()) } }, false /* blockable */)) } @@ -155,17 +156,17 @@ class ControlActionCoordinatorImpl @Inject constructor( bgExecutor.execute { vibrator.vibrate(effect) } } - private fun showDialog(cvh: ControlViewHolder, intent: Intent) { + private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) { bgExecutor.execute { - val activities: List = cvh.context.packageManager.queryIntentActivities( - intent, + val activities: List = context.packageManager.queryIntentActivities( + pendingIntent.getIntent(), PackageManager.MATCH_DEFAULT_ONLY ) uiExecutor.execute { // make sure the intent is valid before attempting to open the dialog if (activities.isNotEmpty()) { - dialog = DetailDialog(cvh, intent).also { + dialog = DetailDialog(cvh, pendingIntent).also { it.setOnDismissListener { _ -> dialog = null } it.show() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index 9ec14523a809..a7f7eb7ffb44 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -16,8 +16,11 @@ package com.android.systemui.controls.ui +import android.app.ActivityOptions import android.app.ActivityView +import android.app.PendingIntent import android.app.Dialog +import android.content.ComponentName import android.content.Intent import android.provider.Settings import android.view.View @@ -37,9 +40,8 @@ import com.android.systemui.R */ class DetailDialog( val cvh: ControlViewHolder, - val intent: Intent + val pendingIntent: PendingIntent ) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) { - companion object { private const val PANEL_TOP_OFFSET = "systemui.controls_panel_top_offset" /* @@ -49,18 +51,19 @@ class DetailDialog( private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL" } + private val fillInIntent = Intent().apply { + putExtra(EXTRA_USE_PANEL, true) + + // Apply flags to make behaviour match documentLaunchMode=always. + addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) + addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + } + var activityView = ActivityView(context, null, 0, false) val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() { override fun onActivityViewReady(view: ActivityView) { - val launchIntent = Intent(intent) - launchIntent.putExtra(EXTRA_USE_PANEL, true) - - // Apply flags to make behaviour match documentLaunchMode=always. - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) - launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - - view.startActivity(launchIntent) + view.startActivity(pendingIntent, fillInIntent, ActivityOptions.makeBasic()) } override fun onActivityViewDestroyed(view: ActivityView) {} @@ -68,6 +71,12 @@ class DetailDialog( override fun onTaskRemovalStarted(taskId: Int) { dismiss() } + + override fun onTaskCreated(taskId: Int, name: ComponentName?) { + requireViewById(R.id.controls_activity_view).apply { + setAlpha(1f) + } + } } init { @@ -76,6 +85,7 @@ class DetailDialog( requireViewById(R.id.controls_activity_view).apply { addView(activityView) + setAlpha(0f) } requireViewById(R.id.control_detail_close).apply { @@ -86,7 +96,7 @@ class DetailDialog( setOnClickListener { v: View -> dismiss() context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) - v.context.startActivity(intent) + pendingIntent.send() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt new file mode 100644 index 000000000000..0ad03d2b41b3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.app.ActivityView +import android.app.PendingIntent +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.capture +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.eq +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class DetailDialogTest : SysuiTestCase() { + + @Mock + private lateinit var activityView: ActivityView + @Mock + private lateinit var controlViewHolder: ControlViewHolder + @Mock + private lateinit var pendingIntent: PendingIntent + @Captor + private lateinit var viewCaptor: ArgumentCaptor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testPendingIntentIsUnModified() { + // GIVEN the dialog is created with a PendingIntent + val dialog = createDialog(pendingIntent) + + // WHEN the ActivityView is initialized + dialog.stateCallback.onActivityViewReady(capture(viewCaptor)) + + // THEN the PendingIntent used to call startActivity is unmodified by systemui + verify(viewCaptor.value).startActivity(eq(pendingIntent), any(), any()) + } + + private fun createDialog(pendingIntent: PendingIntent): DetailDialog { + return DetailDialog( + controlViewHolder, + pendingIntent + ) + } +} From 35d62d87cce8b00ef8ab6ecc8eb8c1c39e273f2c Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Wed, 30 Dec 2020 18:30:25 -0800 Subject: [PATCH 021/208] DO NOT MERGE Re-implement reading/writing Throwables from/to Parcel, without Parcel private APIs. Bug:197228210 Test: atest CtsSecurityTestCases:android.security.cts.AndroidFutureTest (cherry picked from I577da5a3bc4ed537123b7eceaa5addf8f7bb0d92 and Icc5ce702f0cd84e9136dee3c65f63619df697358) Change-Id: I1d488c475f2f7af835a67496535cecdd6987c0cf (cherry picked from commit 562f1bd91f2845788ab907d687de6800ee49c6f8) Merged-In:I1d488c475f2f7af835a67496535cecdd6987c0cf --- .../android/internal/infra/AndroidFuture.java | 116 ++++++++++++------ .../internal/infra/ServiceConnector.java | 28 ++--- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java index 9f15d8991fa7..84391c169941 100644 --- a/core/java/com/android/internal/infra/AndroidFuture.java +++ b/core/java/com/android/internal/infra/AndroidFuture.java @@ -16,23 +16,21 @@ package com.android.internal.infra; -import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; - import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; -import android.os.Message; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import android.util.ExceptionUtils; +import android.util.EventLog; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.android.internal.util.function.pooled.PooledLambda; +import java.lang.reflect.Constructor; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -75,14 +73,16 @@ public class AndroidFuture extends CompletableFuture implements Parcelable private static final boolean DEBUG = false; private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); + private static final Executor DIRECT_EXECUTOR = Runnable::run; private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; + private static @Nullable Handler sMainHandler; private final @NonNull Object mLock = new Object(); @GuardedBy("mLock") private @Nullable BiConsumer mListener; @GuardedBy("mLock") private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR; - private @NonNull Handler mTimeoutHandler = Handler.getMain(); + private @NonNull Handler mTimeoutHandler = getMainHandler(); private final @Nullable IAndroidFuture mRemoteOrigin; public AndroidFuture() { @@ -96,7 +96,7 @@ public AndroidFuture() { // Done if (in.readBoolean()) { // Failed - completeExceptionally(unparcelException(in)); + completeExceptionally(readThrowable(in)); } else { // Success complete((T) in.readValue(null)); @@ -108,6 +108,15 @@ public AndroidFuture() { } } + @NonNull + private static Handler getMainHandler() { + // This isn't thread-safe but we are okay with it. + if (sMainHandler == null) { + sMainHandler = new Handler(Looper.getMainLooper()); + } + return sMainHandler; + } + /** * Create a completed future with the given value. * @@ -236,9 +245,7 @@ private void callListenerAsync(BiConsumer listener if (mListenerExecutor == DIRECT_EXECUTOR) { callListener(listener, res, err); } else { - mListenerExecutor.execute(PooledLambda - .obtainRunnable(AndroidFuture::callListener, listener, res, err) - .recycleOnUse()); + mListenerExecutor.execute(() -> callListener(listener, res, err)); } } @@ -260,7 +267,8 @@ static void callListener( } else { // listener exception-case threw // give up on listener but preserve the original exception when throwing up - throw ExceptionUtils.appendCause(t, err); + t.addSuppressed(err); + throw t; } } } catch (Throwable t2) { @@ -272,9 +280,7 @@ static void callListener( /** @inheritDoc */ //@Override //TODO uncomment once java 9 APIs are exposed to frameworks public AndroidFuture orTimeout(long timeout, @NonNull TimeUnit unit) { - Message msg = PooledLambda.obtainMessage(AndroidFuture::triggerTimeout, this); - msg.obj = this; - mTimeoutHandler.sendMessageDelayed(msg, unit.toMillis(timeout)); + mTimeoutHandler.postDelayed(this::triggerTimeout, this, unit.toMillis(timeout)); return this; } @@ -507,7 +513,7 @@ public void writeToParcel(Parcel dest, int flags) { result = get(); } catch (Throwable t) { dest.writeBoolean(true); - parcelException(dest, unwrapExecutionException(t)); + writeThrowable(dest, unwrapExecutionException(t)); return; } dest.writeBoolean(false); @@ -545,45 +551,75 @@ Throwable unwrapExecutionException(Throwable t) { * Alternative to {@link Parcel#writeException} that stores the stack trace, in a * way consistent with the binder IPC exception propagation behavior. */ - private static void parcelException(Parcel p, @Nullable Throwable t) { - p.writeBoolean(t == null); - if (t == null) { + private static void writeThrowable(@NonNull Parcel parcel, @Nullable Throwable throwable) { + boolean hasThrowable = throwable != null; + parcel.writeBoolean(hasThrowable); + if (!hasThrowable) { + return; + } + + boolean isFrameworkParcelable = throwable instanceof Parcelable + && throwable.getClass().getClassLoader() == Parcelable.class.getClassLoader(); + parcel.writeBoolean(isFrameworkParcelable); + if (isFrameworkParcelable) { + parcel.writeParcelable((Parcelable) throwable, + Parcelable.PARCELABLE_WRITE_RETURN_VALUE); return; } - p.writeInt(Parcel.getExceptionCode(t)); - p.writeString(t.getClass().getName()); - p.writeString(t.getMessage()); - p.writeStackTrace(t); - parcelException(p, t.getCause()); + parcel.writeString(throwable.getClass().getName()); + parcel.writeString(throwable.getMessage()); + StackTraceElement[] stackTrace = throwable.getStackTrace(); + StringBuilder stackTraceBuilder = new StringBuilder(); + int truncatedStackTraceLength = Math.min(stackTrace != null ? stackTrace.length : 0, 5); + for (int i = 0; i < truncatedStackTraceLength; i++) { + if (i > 0) { + stackTraceBuilder.append('\n'); + } + stackTraceBuilder.append("\tat ").append(stackTrace[i]); + } + parcel.writeString(stackTraceBuilder.toString()); + writeThrowable(parcel, throwable.getCause()); } /** - * @see #parcelException + * @see #writeThrowable */ - private static @Nullable Throwable unparcelException(Parcel p) { - if (p.readBoolean()) { + private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) { + final boolean hasThrowable = parcel.readBoolean(); + if (!hasThrowable) { return null; } - int exCode = p.readInt(); - String cls = p.readString(); - String msg = p.readString(); - String stackTrace = p.readInt() > 0 ? p.readString() : "\t"; - msg += "\n" + stackTrace; - - Exception ex = p.createExceptionOrNull(exCode, msg); - if (ex == null) { - ex = new RuntimeException(cls + ": " + msg); + boolean isFrameworkParcelable = parcel.readBoolean(); + if (isFrameworkParcelable) { + return parcel.readParcelable(Parcelable.class.getClassLoader()); } - ex.setStackTrace(EMPTY_STACK_TRACE); - Throwable cause = unparcelException(p); + String className = parcel.readString(); + String message = parcel.readString(); + String stackTrace = parcel.readString(); + String messageWithStackTrace = message + '\n' + stackTrace; + Throwable throwable; + try { + Class clazz = Class.forName(className, true, Parcelable.class.getClassLoader()); + if (Throwable.class.isAssignableFrom(clazz)) { + Constructor constructor = clazz.getConstructor(String.class); + throwable = (Throwable) constructor.newInstance(messageWithStackTrace); + } else { + android.util.EventLog.writeEvent(0x534e4554, "186530450", -1, ""); + throwable = new RuntimeException(className + ": " + messageWithStackTrace); + } + } catch (Throwable t) { + throwable = new RuntimeException(className + ": " + messageWithStackTrace); + throwable.addSuppressed(t); + } + throwable.setStackTrace(EMPTY_STACK_TRACE); + Throwable cause = readThrowable(parcel); if (cause != null) { - ex.initCause(ex); + throwable.initCause(cause); } - - return ex; + return throwable; } @Override diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java index 167d128a76f8..89869389cb59 100644 --- a/core/java/com/android/internal/infra/ServiceConnector.java +++ b/core/java/com/android/internal/infra/ServiceConnector.java @@ -26,14 +26,11 @@ import android.os.Handler; import android.os.IBinder; import android.os.IInterface; +import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; -import android.text.TextUtils; -import android.util.DebugUtils; import android.util.Log; -import com.android.internal.util.function.pooled.PooledLambda; - import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; @@ -47,7 +44,6 @@ import java.util.function.BiConsumer; import java.util.function.Function; - /** * Takes care of managing a {@link ServiceConnection} and auto-disconnecting from the service upon * a certain timeout. @@ -220,6 +216,7 @@ class Impl extends ArrayDeque> private final @NonNull Queue> mQueue = this; private final @NonNull List> mUnfinishedJobs = new ArrayList<>(); + private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper()); private final @NonNull ServiceConnection mServiceConnection = this; private final @NonNull Runnable mTimeoutDisconnect = this; @@ -250,9 +247,8 @@ class Impl extends ArrayDeque> * {@link IInterface}. * Typically this is {@code IMyInterface.Stub::asInterface} */ - public Impl(@NonNull Context context, @NonNull Intent intent, - @Context.BindServiceFlags int bindingFlags, @UserIdInt int userId, - @Nullable Function binderAsInterface) { + public Impl(@NonNull Context context, @NonNull Intent intent, int bindingFlags, + @UserIdInt int userId, @Nullable Function binderAsInterface) { mContext = context; mIntent = intent; mBindingFlags = bindingFlags; @@ -264,7 +260,7 @@ public Impl(@NonNull Context context, @NonNull Intent intent, * {@link Handler} on which {@link Job}s will be called */ protected Handler getJobHandler() { - return Handler.getMain(); + return mMainHandler; } /** @@ -391,8 +387,7 @@ private void enqueue(@NonNull CompletionAwareJob task) { private boolean enqueue(@NonNull Job job) { cancelTimeout(); - return getJobHandler().sendMessage(PooledLambda.obtainMessage( - ServiceConnector.Impl::enqueueJobThread, this, job)); + return getJobHandler().post(() -> enqueueJobThread(job)); } void enqueueJobThread(@NonNull Job job) { @@ -422,7 +417,7 @@ private void cancelTimeout() { if (DEBUG) { logTrace(); } - Handler.getMain().removeCallbacks(mTimeoutDisconnect); + mMainHandler.removeCallbacks(mTimeoutDisconnect); } void completeExceptionally(@NonNull Job job, @NonNull Throwable ex) { @@ -486,7 +481,7 @@ private void scheduleUnbindTimeout() { } long timeout = getAutoDisconnectTimeoutMs(); if (timeout > 0) { - Handler.getMain().postDelayed(mTimeoutDisconnect, timeout); + mMainHandler.postDelayed(mTimeoutDisconnect, timeout); } else if (DEBUG) { Log.i(LOG_TAG, "Not scheduling unbind for permanently bound " + this); } @@ -502,7 +497,7 @@ public void unbind() { logTrace(); } mUnbinding = true; - getJobHandler().sendMessage(PooledLambda.obtainMessage(Impl::unbindJobThread, this)); + getJobHandler().post(this::unbindJobThread); } void unbindJobThread() { @@ -659,10 +654,7 @@ private String stateToString() { } private void logTrace() { - Log.i(LOG_TAG, - TextUtils.join(" -> ", - DebugUtils.callersWithin(ServiceConnector.class, /* offset= */ 1)) - + "(" + this + ")"); + Log.i(LOG_TAG, "See stacktrace", new Throwable()); } /** From e603830f76b29229ec83cf77e3d26d756e18ca59 Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Tue, 26 Jan 2021 08:46:31 -0800 Subject: [PATCH 022/208] Don't crash if default supervision profile owner is not set Bug: 175430552 Bug: 197399948 Test: build, flash, watch logcat Change-Id: If441946fa278c04ae88122f6243f5a7dedd96ebc Merged-In: If441946fa278c04ae88122f6243f5a7dedd96ebc (cherry picked from commit bd27c365915f824824d865668c63a2119c4700fb) (cherry picked from commit bcb5ee6f43ea24fb74649f164ca3c3032a7be3b5) (cherry picked from commit b5fa0a6c5e96c420c1f6d808be603c4579f9a1ba) Merged-In:If441946fa278c04ae88122f6243f5a7dedd96ebc --- .../server/devicepolicy/DevicePolicyManagerService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 13fc10cc2707..118350f4e9fd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -9424,6 +9424,9 @@ && isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) { final ComponentName doComponent = mOwners.getDeviceOwnerComponent(); final ComponentName poComponent = mOwners.getProfileOwnerComponent(userHandle.getIdentifier()); + if (supervisorComponent == null) { + return null; + } if (supervisorComponent.equals(doComponent) || supervisorComponent.equals( poComponent)) { return supervisorComponent; From dd7645abf6caaa5501f171489b075f55899eb2c6 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Mon, 3 Jan 2022 15:43:54 +0800 Subject: [PATCH 023/208] Don't abandon child sessions (1/n) It will throw if abandon() is called on a child session. Bug: 211944991 Bug: 67862680 Test: to be added Change-Id: Ib0ba9f3786dda2d3174f3ea8c65d1061a3fcb586 Merged-In: Ib0ba9f3786dda2d3174f3ea8c65d1061a3fcb586 (cherry picked from commit 8b67e7db79d15ab448ae8f00b40a4a350ab3d537) (cherry picked from commit c685f8b19adcec0dc49ffaa1e94d7caa4f9d05ba) Merged-In:Ib0ba9f3786dda2d3174f3ea8c65d1061a3fcb586 --- .../java/com/android/server/pm/PackageInstallerService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 30fed643b3fa..2439b3722d75 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -344,7 +344,11 @@ public void freeStageDirs(String volumeUuid) { if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) { // Aggressively close old sessions because we are running low on storage // Their staging dirs will be removed too - session.abandon(); + PackageInstallerSession root = !session.hasParentSessionId() + ? session : mSessions.get(session.getParentSessionId()); + if (!root.isDestroyed()) { + root.abandon(); + } } else { // Session is new enough, so it deserves to be kept even on low storage unclaimedStagingDirsOnVolume.remove(session.stageDir); From c7fbb93765dfc8d35919db844caea290fad909c3 Mon Sep 17 00:00:00 2001 From: Jackal Guo Date: Fri, 24 Dec 2021 11:47:47 +0800 Subject: [PATCH 024/208] [RESTRICT AUTOMERGE] Fix the inconsistency of protection level The value may be inconsistent between BasePermission.perm.protection- Level and BasePermission.getProtectionLevel() within the same object. Update the perm to fix the inconsistency. Bug: 209607944 Test: manually using the PoC on the buganizer to ensure the symptom no longer exists. Change-Id: I19d6135f98bee9392d85e56478c42e06cfea8ba5 (cherry picked from commit b5efdf729385cc54f225496d3ba20f1cb5b68250) Merged-In:I19d6135f98bee9392d85e56478c42e06cfea8ba5 --- .../java/com/android/server/pm/permission/BasePermission.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 5e04171a3bca..354f5b9a8bdf 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -409,6 +409,8 @@ static BasePermission createOrUpdate(PackageManagerInternal packageManagerIntern } if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName()) && Objects.equals(bp.perm.getName(), p.getName())) { + bp.perm.setFlags(bp.perm.getFlags() & ~PermissionInfo.FLAG_INSTALLED); + bp.perm = p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); bp.protectionLevel = p.getProtectionLevel(); } if (bp.isRuntime() && (ownerChanged || wasNonRuntime)) { From 955376ff31a218eb02f5c1cdb3309f8b80f8838c Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 7 Jan 2022 16:28:52 -0500 Subject: [PATCH 025/208] Check group channels for FGSes Before allowing the group to be deleted, by updating the current check to the method that populates the channel list Test: NotificationManagerServiceTest Bug: 209965481 Change-Id: I9db781c300e96e9c80bd5d21585b8be9b4db08c8 Merged-In: I9db781c300e96e9c80bd5d21585b8be9b4db08c8 (cherry picked from commit 6456b622fd39115001478b6fad2f45f50b65f30a) Merged-In:I9db781c300e96e9c80bd5d21585b8be9b4db08c8 --- .../NotificationManagerService.java | 5 +- .../NotificationManagerServiceTest.java | 63 +++++++++++++++++-- 2 files changed, 62 insertions(+), 6 deletions(-) mode change 100755 => 100644 services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e967e59ebe0b..eb91a663d18a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2504,7 +2504,7 @@ private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel pr } } - private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, + void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, boolean fromApp, boolean fromListener) { Objects.requireNonNull(group); Objects.requireNonNull(pkg); @@ -3537,7 +3537,8 @@ public void deleteNotificationChannelGroup(String pkg, String groupId) { final int callingUid = Binder.getCallingUid(); NotificationChannelGroup groupToDelete = - mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid); + mPreferencesHelper.getNotificationChannelGroupWithChannels( + pkg, callingUid, groupId, false); if (groupToDelete != null) { // Preflight for allowability final int userId = UserHandle.getUserId(callingUid); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java old mode 100755 new mode 100644 index 1a4cce466c66..c0ecbbadb695 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -179,6 +179,8 @@ import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; +import com.google.common.collect.ImmutableList; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -202,6 +204,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -245,6 +248,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock Resources mResources; @Mock + ActivityManagerInternal mAmi; + @Mock RankingHandler mRankingHandler; @Mock protected PackageManagerInternal mPackageManagerInternal; @@ -418,7 +423,6 @@ public void setUp() throws Exception { DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class); when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L); - ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class); LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); @@ -429,7 +433,7 @@ public void setUp() throws Exception { LocalServices.removeServiceForTest(DeviceIdleInternal.class); LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal); LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal); + LocalServices.addService(ActivityManagerInternal.class, mAmi); mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager); @@ -506,7 +510,7 @@ null, new ComponentName(PKG, "test_class"), mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), - mock(ActivityManagerInternal.class)); + mAmi); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mService.setAudioManager(mAudioManager); @@ -2473,7 +2477,8 @@ public void testDeleteChannelGroupNotifyListener() throws Exception { .thenReturn(associations); NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c"); mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt())) + when(mPreferencesHelper.getNotificationChannelGroupWithChannels( + eq(PKG), anyInt(), eq(ncg.getId()), anyBoolean())) .thenReturn(ncg); reset(mListeners); mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId()); @@ -2482,6 +2487,56 @@ public void testDeleteChannelGroupNotifyListener() throws Exception { eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED)); } + @Test + public void testDeleteChannelGroupChecksForFgses() throws Exception { + List associations = new ArrayList<>(); + associations.add("a"); + when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) + .thenReturn(associations); + CountDownLatch latch = new CountDownLatch(2); + mService.createNotificationChannelGroup( + PKG, mUid, new NotificationChannelGroup("group", "group"), true, false); + new Thread(() -> { + NotificationChannel notificationChannel = new NotificationChannel("id", "id", + NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup("group"); + ParceledListSlice pls = + new ParceledListSlice(ImmutableList.of(notificationChannel)); + try { + mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + latch.countDown(); + }).start(); + new Thread(() -> { + try { + synchronized (this) { + wait(5000); + } + mService.createNotificationChannelGroup(PKG, mUid, + new NotificationChannelGroup("new", "new group"), true, false); + NotificationChannel notificationChannel = + new NotificationChannel("id", "id", NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup("new"); + ParceledListSlice pls = + new ParceledListSlice(ImmutableList.of(notificationChannel)); + try { + mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls); + mBinderService.deleteNotificationChannelGroup(PKG, "group"); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } catch (Exception e) { + e.printStackTrace(); + } + latch.countDown(); + }).start(); + + latch.await(); + verify(mAmi).hasForegroundServiceNotification(anyString(), anyInt(), anyString()); + } + @Test public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); From 24ed6e0a7ed1426caffe5d944d6d45af64fdaafb Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Tue, 11 Jan 2022 11:58:59 -0500 Subject: [PATCH 026/208] Handle onNullBinding According to the docs, an onNullBinding requires the service to be manually unbound. Test: test apk that return null on onBind Test: atest ControlsProviderLifecycleManager Fixes: 212286849 Change-Id: I71a59b875bbf9eb411e6e92ddc5a04a7353a46c4 (cherry picked from commit d0e683b8f65ba43e596911324bbff2e4f9909303) Merged-In:I71a59b875bbf9eb411e6e92ddc5a04a7353a46c4 --- .../ControlsProviderLifecycleManager.kt | 6 +++ .../ControlsProviderLifecycleManagerTest.kt | 50 +++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 977e46ac3b44..d2ded71487dc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -131,6 +131,12 @@ class ControlsProviderLifecycleManager( wrapper = null bindService(false) } + + override fun onNullBinding(name: ComponentName?) { + if (DEBUG) Log.d(TAG, "onNullBinding $name") + wrapper = null + context.unbindService(this) + } } private fun handlePendingServiceMethods() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index 2d3757c29ebf..12096bc06748 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.controls.controller import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection import android.os.UserHandle import android.service.controls.IControlsActionCallback import android.service.controls.IControlsProvider @@ -43,6 +46,8 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -57,8 +62,6 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { private lateinit var subscriberService: IControlsSubscriber.Stub @Mock private lateinit var service: IControlsProvider.Stub - @Mock - private lateinit var loadCallback: ControlsBindingController.LoadCallback @Captor private lateinit var wrapperCaptor: ArgumentCaptor @@ -75,7 +78,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - mContext.addMockService(componentName, service) + context.addMockService(componentName, service) executor = FakeExecutor(FakeSystemClock()) `when`(service.asBinder()).thenCallRealMethod() `when`(service.queryLocalInterface(ArgumentMatchers.anyString())).thenReturn(service) @@ -98,7 +101,36 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun testBindService() { manager.bindService() executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) + } + + @Test + fun testNullBinding() { + val mockContext = mock(Context::class.java) + lateinit var serviceConnection: ServiceConnection + `when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer { + val component = (it.arguments[0] as Intent).component + if (component == componentName) { + serviceConnection = it.arguments[1] as ServiceConnection + serviceConnection.onNullBinding(component) + true + } else { + false + } + } + + val nullManager = ControlsProviderLifecycleManager( + mockContext, + executor, + actionCallbackService, + UserHandle.of(0), + componentName + ) + + nullManager.bindService() + executor.runAllReady() + + verify(mockContext).unbindService(serviceConnection) } @Test @@ -109,7 +141,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -119,7 +151,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { verify(service).load(subscriberService) - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) } @Test @@ -129,7 +161,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -162,7 +194,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSubscribe(list, subscriberService) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).subscribe(list, subscriberService) } @@ -173,7 +205,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSendAction(controlId, action) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).action(eq(controlId), capture(wrapperCaptor), eq(actionCallbackService)) assertEquals(action, wrapperCaptor.getValue().getWrappedAction()) From 579a7eff3e90674c875b8c9780747f1b0cef7fdc Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 20 Jul 2021 16:17:58 -0700 Subject: [PATCH 027/208] Only allow trusted overlays to specify FLAG_SLIPPERY For all other requests, drop this flag. Test: atest FlagSlipperyTest Bug: 157929241 Change-Id: Ia30f1c38d5ddb351c90b748ea76448a76a9dde7b Merged-In: Ia30f1c38d5ddb351c90b748ea76448a76a9dde7b (cherry picked from commit 07e7aaff2957c103d1bcd51e6e9b1dbde29d87bd) Merged-In:Ia30f1c38d5ddb351c90b748ea76448a76a9dde7b --- .../com/android/server/wm/DisplayPolicy.java | 30 ++++++++++--------- .../server/wm/WindowManagerService.java | 6 ++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 97e4a9595da3..b5b94c65555d 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -65,6 +65,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -908,6 +909,20 @@ private boolean hasStatusBarServicePermission(int pid, int uid) { == PackageManager.PERMISSION_GRANTED; } + /** + * Only trusted overlays are allowed to use FLAG_SLIPPERY. + */ + static int sanitizeFlagSlippery(int flags, int privateFlags, String name) { + if ((flags & FLAG_SLIPPERY) == 0) { + return flags; + } + if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { + return flags; + } + Slog.w(TAG, "Removing FLAG_SLIPPERY for non-trusted overlay " + name); + return flags & ~FLAG_SLIPPERY; + } + /** * Sanitize the layout parameters coming from a client. Allows the policy * to do things like ensure that windows of a specific type can't take @@ -991,20 +1006,7 @@ public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams att } break; } - - // Check if alternate bars positions were updated. - if (mStatusBarAlt == win) { - mStatusBarAltPosition = getAltBarPosition(attrs); - } - if (mNavigationBarAlt == win) { - mNavigationBarAltPosition = getAltBarPosition(attrs); - } - if (mClimateBarAlt == win) { - mClimateBarAltPosition = getAltBarPosition(attrs); - } - if (mExtraNavBarAlt == win) { - mExtraNavBarAltPosition = getAltBarPosition(attrs); - } + attrs.flags = sanitizeFlagSlippery(attrs.flags, attrs.privateFlags, win.getName()); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 355a531496f3..6017b59d4565 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -56,6 +56,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; @@ -8095,8 +8096,9 @@ private void updateInputChannel(IBinder channelToken, int callingUid, int callin h.token = channelToken; h.name = name; - final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE - | LayoutParams.FLAG_SLIPPERY); + flags = DisplayPolicy.sanitizeFlagSlippery(flags, privateFlags, name); + + final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY); h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; h.layoutParamsType = type; h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; From 44378eb68dcabdaff84d613e8580be39babe6f41 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 29 Nov 2021 09:07:25 -0800 Subject: [PATCH 028/208] Add ALLOW_SLIPPERY_TOUCHES permission This permission allows the app to use FLAG_SLIPPERY. This means, windows of the app that has this permission can let touches slip out when the finger moves out of the window bounds. Bug: 157929241 Bug: 206188649 Test: atest FlagSlipperyTest Change-Id: I9ccdfd298f32c36b9c4da68c2e9c355c97dc7593 Merged-In: I9ccdfd298f32c36b9c4da68c2e9c355c97dc7593 (cherry picked from commit cccf19150f5247e101417b2a4f3748813dd7058a) Merged-In:I9ccdfd298f32c36b9c4da68c2e9c355c97dc7593 --- core/res/AndroidManifest.xml | 4 ++++ packages/SystemUI/AndroidManifest.xml | 1 + .../com/android/server/wm/DisplayPolicy.java | 16 -------------- .../server/wm/WindowManagerService.java | 21 ++++++++++++++++++- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5a01420732e9..9aa9482f67a8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5039,6 +5039,10 @@ + + + diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index b5b94c65555d..4f828df3d1e0 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -65,7 +65,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -909,20 +908,6 @@ private boolean hasStatusBarServicePermission(int pid, int uid) { == PackageManager.PERMISSION_GRANTED; } - /** - * Only trusted overlays are allowed to use FLAG_SLIPPERY. - */ - static int sanitizeFlagSlippery(int flags, int privateFlags, String name) { - if ((flags & FLAG_SLIPPERY) == 0) { - return flags; - } - if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { - return flags; - } - Slog.w(TAG, "Removing FLAG_SLIPPERY for non-trusted overlay " + name); - return flags & ~FLAG_SLIPPERY; - } - /** * Sanitize the layout parameters coming from a client. Allows the policy * to do things like ensure that windows of a specific type can't take @@ -1006,7 +991,6 @@ public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams att } break; } - attrs.flags = sanitizeFlagSlippery(attrs.flags, attrs.privateFlags, win.getName()); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6017b59d4565..c25fd12266af 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1571,6 +1571,7 @@ public int addWindow(Session session, IWindow client, int seq, final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); + attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid); res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); if (res != WindowManagerGlobal.ADD_OKAY) { @@ -2155,6 +2156,7 @@ public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams if (attrs != null) { displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); win.mToken.adjustWindowParams(win, attrs); + attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid); // if they don't have the permission, mask out the status bar bits if (seq == win.mSeq) { int systemUiVisibility = attrs.systemUiVisibility @@ -8059,6 +8061,23 @@ void handleTaskFocusChange(Task task) { } } + /** + * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY. + */ + private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) { + if ((flags & FLAG_SLIPPERY) == 0) { + return flags; + } + final int permissionResult = mContext.checkPermission( + android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, callingPid, callingUid); + if (permissionResult != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Removing FLAG_SLIPPERY from '" + windowName + + "' because it doesn't have ALLOW_SLIPPERY_TOUCHES permission"); + return flags & ~FLAG_SLIPPERY; + } + return flags; + } + /** * Assigns an InputChannel to a SurfaceControl and configures it to receive * touch input according to it's on-screen geometry. @@ -8096,7 +8115,7 @@ private void updateInputChannel(IBinder channelToken, int callingUid, int callin h.token = channelToken; h.name = name; - flags = DisplayPolicy.sanitizeFlagSlippery(flags, privateFlags, name); + flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid); final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY); h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; From 693fcb95508d86766eb255db7f7a65135652f0bf Mon Sep 17 00:00:00 2001 From: "Kevin F. Haggerty" Date: Tue, 5 Apr 2022 06:38:07 -0600 Subject: [PATCH 029/208] Revert "Revert "Revert "BG-FGS-start while-in-use permission restriction improve...""" Upstream change 5d30b7015879 ("BG-FGS-start while-in-use permission restriction improvement") properly fixes what we needed to work-around. This reverts commit b64352d7c66e93ddc2b448f556e265178309d730. Change-Id: I00b3ab4161dceddff015e4f5365cd777acfe2faa --- .../com/android/server/am/ActiveServices.java | 107 ++++-------------- .../server/am/ActivityManagerConstants.java | 20 ---- .../com/android/server/am/ServiceRecord.java | 6 - 3 files changed, 21 insertions(+), 112 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 1cd7115f8edc..e4dd8756fe62 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -734,8 +734,11 @@ ComponentName startServiceLocked(IApplicationThread caller, Intent service, Stri } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r, - allowBackgroundActivityStarts); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = + shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, + callingUid, service, r, allowBackgroundActivityStarts); + } return cmp; } @@ -1408,6 +1411,14 @@ private void setServiceForegroundInnerLocked(final ServiceRecord r, int id, + String.format("0x%08X", manifestType) + " in service element of manifest file"); } + // If the foreground service is not started from TOP process, do not allow it to + // have while-in-use location/camera/microphone access. + if (!r.mAllowWhileInUsePermissionInFgs) { + Slog.w(TAG, + "Foreground service started from background can not have " + + "location/camera/microphone access: service " + + r.shortInstanceName); + } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; @@ -1455,57 +1466,6 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { ignoreForeground = true; } - if (!ignoreForeground) { - if (r.mStartForegroundCount == 0) { - /* - If the service was started with startService(), not - startForegroundService(), and if startForeground() isn't called within - mFgsStartForegroundTimeoutMs, then we check the state of the app - (who owns the service, which is the app that called startForeground()) - again. If the app is in the foreground, or in any other cases where - FGS-starts are allowed, then we still allow the FGS to be started. - Otherwise, startForeground() would fail. - - If the service was started with startForegroundService(), then the service - must call startForeground() within a timeout anyway, so we don't need this - check. - */ - if (!r.fgRequired) { - final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime; - if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { - resetFgsRestrictionLocked(r); - setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, - r.appInfo.uid, r, false); - EventLog.writeEvent(0x534e4554, "183147114", - r.appInfo.uid, - "call setFgsRestrictionLocked again due to " - + "startForegroundTimeout"); - } - } - } else if (r.mStartForegroundCount >= 1) { - // The second or later time startForeground() is called after service is - // started. Check for app state again. - final long delayMs = SystemClock.elapsedRealtime() - - r.mLastSetFgsRestrictionTime; - if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { - resetFgsRestrictionLocked(r); - setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, - r.appInfo.uid, r, false); - EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid, - "call setFgsRestrictionLocked for " - + (r.mStartForegroundCount + 1) + "th startForeground"); - } - } - // If the foreground service is not started from TOP process, do not allow it to - // have while-in-use location/camera/microphone access. - if (!r.mAllowWhileInUsePermissionInFgs) { - Slog.w(TAG, - "Foreground service started from background can not have " - + "location/camera/microphone access: service " - + r.shortInstanceName); - } - } - // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -1541,7 +1501,6 @@ must call startForeground() within a timeout anyway, so we don't need this active.mNumActive++; } r.isForeground = true; - r.mStartForegroundCount++; if (!stopProcStatsOp) { ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1600,7 +1559,6 @@ must call startForeground() within a timeout anyway, so we don't need this decActiveForegroundAppLocked(smap, r); } r.isForeground = false; - resetFgsRestrictionLocked(r); ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), @@ -2160,7 +2118,12 @@ public void run() { } } - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false); + if (!s.mAllowWhileInUsePermissionInFgs) { + s.mAllowWhileInUsePermissionInFgs = + shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, + callingPid, callingUid, + service, s, false); + } if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -3456,7 +3419,7 @@ private final void bringDownServiceLocked(ServiceRecord r) { r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; - resetFgsRestrictionLocked(r); + r.mAllowWhileInUsePermissionInFgs = false; // Clear start entries. r.clearDeliveredStartsLocked(); @@ -4937,7 +4900,7 @@ private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw, * @return true if allow, false otherwise. */ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, - int callingPid, int callingUid, ServiceRecord r, + int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { // Is the background FGS start restriction turned on? if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { @@ -5019,32 +4982,4 @@ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage } return false; } - - boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid, - String callingPackage) { - return shouldAllowWhileInUsePermissionInFgsLocked( - callingPackage, callingPid, callingUid, null, false); - } - - /** - * In R, mAllowWhileInUsePermissionInFgs is to 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 r the service to start. - * @return true if allow, false otherwise. - */ - private void setFgsRestrictionLocked(String callingPackage, - int callingPid, int callingUid, ServiceRecord r, - boolean allowBackgroundActivityStarts) { - r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime(); - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked( - callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); - } - } - - private void resetFgsRestrictionLocked(ServiceRecord r) { - r.mAllowWhileInUsePermissionInFgs = false; - } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 00d8208ea118..7be843f17863 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -88,7 +88,6 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_PROCESS_START_ASYNC = "process_start_async"; static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; - static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -122,7 +121,6 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_PROCESS_START_ASYNC = true; private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; - private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; // Flag stored in the DeviceConfig API. @@ -275,12 +273,6 @@ final class ActivityManagerConstants extends ContentObserver { // this long. public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; - /** - * When service started from background, before the timeout it can be promoted to FGS by calling - * Service.startForeground(). - */ - volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS; - // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; @@ -429,9 +421,6 @@ public void onPropertiesChanged(Properties properties) { case KEY_MIN_ASSOC_LOG_DURATION: updateMinAssocLogDuration(); break; - case KEY_FGS_START_FOREGROUND_TIMEOUT: - updateFgsStartForegroundTimeout(); - break; default: break; } @@ -708,13 +697,6 @@ private void updateMinAssocLogDuration() { /* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION); } - private void updateFgsStartForegroundTimeout() { - mFgsStartForegroundTimeoutMs = DeviceConfig.getLong( - DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - KEY_FGS_START_FOREGROUND_TIMEOUT, - DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS); - } - void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":"); @@ -787,8 +769,6 @@ void dump(PrintWriter pw) { pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray())); pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("="); pw.println(MIN_ASSOC_LOG_DURATION); - pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("="); - pw.println(mFgsStartForegroundTimeoutMs); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 0e628289a09f..1b65dbac2294 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -142,10 +142,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; - // The number of times Service.startForeground() is called; - int mStartForegroundCount; - // Last time mAllowWhileInUsePermissionInFgs is set. - long mLastSetFgsRestrictionTime; // the most recent package that start/bind this service. String mRecentCallingPackage; @@ -410,8 +406,6 @@ void dump(PrintWriter pw, String prefix) { } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); - pw.print(prefix); pw.print("startForegroundCount="); - pw.println(mStartForegroundCount); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); if (delayed) { From 4fdd2d2c1bd39f1f319d54ca89543efa3a24eebd Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 12 Jan 2022 15:08:29 -0500 Subject: [PATCH 030/208] Prevent apps from creating blocked channel groups setBlocked is a hidden API, so apps should not be calling the method, but fix up the data in case they do Test: PreferencesHelperTest; manual with ApiDemos FGS Bug: 209966086 Change-Id: Icc709a6b0d0a8c5f2d9243959992f1b6764354db Merged-In: I8a27853c7ed05d9dfd38a3142fbbe185946c3992 (cherry picked from commit c5b545329fad3da10a6640995f6110013ad8ff5a) Merged-In:Icc709a6b0d0a8c5f2d9243959992f1b6764354db --- .../server/notification/PreferencesHelper.java | 3 +++ .../notification/PreferencesHelperTest.java | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 5f14e5146bd0..7a93edeaf664 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -788,6 +788,9 @@ public void createNotificationChannelGroup(String pkg, int uid, NotificationChan if (r == null) { throw new IllegalArgumentException("Invalid package"); } + if (fromTargetApp) { + group.setBlocked(false); + } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (oldGroup != null) { group.setChannels(oldGroup.getChannels()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 6aeb40dae20b..06cfbea72a82 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2132,6 +2132,19 @@ public void testIsGroupBlocked_blocked() throws Exception { assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); } + @Test + public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception { + NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); + group.setBlocked(true); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + + NotificationChannelGroup group3 = group.clone(); + group3.setBlocked(false); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + } + @Test public void testIsGroup_appCannotResetBlock() throws Exception { NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); @@ -3402,7 +3415,7 @@ public void testGetConversations_noConversations() { public void testGetConversations_noDisabledGroups() { NotificationChannelGroup group = new NotificationChannelGroup("a", "a"); group.setBlocked(true); - mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false); NotificationChannel parent = new NotificationChannel("parent", "p", 1); mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false); From 97b4f1a8c343dfbc66722cc7e7e4d0ce65d6dfe8 Mon Sep 17 00:00:00 2001 From: Joshua Duong Date: Wed, 22 Dec 2021 14:49:21 -0800 Subject: [PATCH 031/208] Restrict AdbManager broadcasts to apps with MANAGE_DEBUGGING permission. Bug: 205836329 Test: atest AdbDebuggingManagerTest Change-Id: If18a874c6d6232d9131f2cc3de3614ef67a58bbd (cherry picked from commit b139e9966102ba6fa1a4801214ea92e8afe376a6) (cherry picked from commit 398b752a440f7d60198f9267334445aba4f9d4eb) Merged-In:If18a874c6d6232d9131f2cc3de3614ef67a58bbd --- core/java/android/debug/AdbManager.java | 3 + .../server/adb/AdbDebuggingManager.java | 21 +++- .../com/android/server/adb/AdbService.java | 2 +- .../server/adb/AdbDebuggingManagerTest.java | 110 ++++++++++++++++++ 4 files changed, 129 insertions(+), 7 deletions(-) diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java index 7714dd80f910..243f80187185 100644 --- a/core/java/android/debug/AdbManager.java +++ b/core/java/android/debug/AdbManager.java @@ -38,6 +38,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION = "com.android.server.adb.WIRELESS_DEBUG_STATUS"; @@ -46,6 +47,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION = "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES"; @@ -59,6 +61,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION = "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT"; diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index ed83a644cbfb..95da946496e8 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -18,6 +18,7 @@ import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull; +import android.annotation.NonNull; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.Notification; @@ -171,6 +172,12 @@ protected AdbDebuggingManager(Context context, String confirmComponent, File tes mAdbConnectionInfo = new AdbConnectionInfo(); } + static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent, + @NonNull UserHandle userHandle) { + context.sendBroadcastAsUser(intent, userHandle, + android.Manifest.permission.MANAGE_DEBUGGING); + } + class PairingThread extends Thread implements NsdManager.RegistrationListener { private NsdManager mNsdManager; private String mPublicKey; @@ -1279,7 +1286,7 @@ private void sendServerConnectionState(boolean connected, int port) { ? AdbManager.WIRELESS_STATUS_CONNECTED : AdbManager.WIRELESS_STATUS_DISCONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void onAdbdWifiServerConnected(int port) { @@ -1351,7 +1358,8 @@ private void onPairingResult(String publicKey) { if (publicKey == null) { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, + UserHandle.ALL); } else { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, @@ -1364,7 +1372,8 @@ private void onPairingResult(String publicKey) { } PairDevice device = new PairDevice(fingerprints, hostname, false); intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, + UserHandle.ALL); // Add the key into the keystore mAdbKeyStore.setLastConnectionTime(publicKey, System.currentTimeMillis()); @@ -1378,14 +1387,14 @@ private void sendPairingPortToUI(int port) { intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_CONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void sendPairedDevicesToUI(Map devices) { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); // Map is not serializable, so need to downcast intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void updateUIPairCode(String code) { @@ -1395,7 +1404,7 @@ private void updateUIPairCode(String code) { intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_PAIRING_CODE); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } } diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 29bb5428dd84..5b16daa5e835 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -431,7 +431,7 @@ private void broadcastPortInfo(int port) { ? AdbManager.WIRELESS_STATUS_CONNECTED : AdbManager.WIRELESS_STATUS_DISCONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); Slog.i(TAG, "sent port broadcast port=" + port); } diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java index cffff66b64f1..02cf971a8076 100644 --- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java @@ -23,7 +23,14 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.debug.AdbManager; +import android.debug.IAdbManager; +import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; @@ -105,6 +112,7 @@ public void setUp() throws Exception { public void tearDown() throws Exception { mKeyStore.deleteKeyStore(); setAllowedConnectionTime(mOriginalAllowedConnectionTime); + dropShellPermissionIdentity(); } /** @@ -813,6 +821,108 @@ private boolean isValidMdnsServiceName(String name) { return hasAtLeastOneLetter; } + CountDownLatch mAdbActionLatch = new CountDownLatch(1); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.i(TAG, "Received intent action=" + action); + if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + Log.i(TAG, "action=" + action + " paired_device=" + intent.getSerializableExtra( + AdbManager.WIRELESS_DEVICES_EXTRA).toString()); + mAdbActionLatch.countDown(); + } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_DISCONNECTED); + Log.i(TAG, "action=" + action + " status=" + status); + mAdbActionLatch.countDown(); + } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + Integer res = intent.getIntExtra( + AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_FAIL); + Log.i(TAG, "action=" + action + " result=" + res); + + if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) { + String pairingCode = intent.getStringExtra( + AdbManager.WIRELESS_PAIRING_CODE_EXTRA); + Log.i(TAG, "pairingCode=" + pairingCode); + } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) { + int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0); + Log.i(TAG, "port=" + port); + } + mAdbActionLatch.countDown(); + } + } + }; + + private void adoptShellPermissionIdentity() { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .adoptShellPermissionIdentity(android.Manifest.permission.MANAGE_DEBUGGING); + } + + private void dropShellPermissionIdentity() { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .dropShellPermissionIdentity(); + } + + @Test + public void testBroadcastReceiverWithPermissions() throws Exception { + adoptShellPermissionIdentity(); + final IAdbManager mAdbManager = IAdbManager.Stub.asInterface( + ServiceManager.getService(Context.ADB_SERVICE)); + IntentFilter intentFilter = + new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + assertEquals("Context does not have MANAGE_DEBUGGING permission.", + mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + try { + mContext.registerReceiver(mReceiver, intentFilter); + mAdbManager.enablePairingByPairingCode(); + if (!mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) { + fail("Receiver did not receive adb intent action within the timeout duration"); + } + } finally { + mContext.unregisterReceiver(mReceiver); + } + } + + @Test + public void testBroadcastReceiverWithoutPermissions() throws Exception { + adoptShellPermissionIdentity(); + final IAdbManager mAdbManager = IAdbManager.Stub.asInterface( + ServiceManager.getService(Context.ADB_SERVICE)); + IntentFilter intentFilter = + new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + mAdbManager.enablePairingByPairingCode(); + + dropShellPermissionIdentity(); + assertEquals("Context has MANAGE_DEBUGGING permission.", + mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_DENIED); + try { + mContext.registerReceiver(mReceiver, intentFilter); + + if (mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) { + fail("Broadcast receiver received adb action intent without debug permissions"); + } + } finally { + mContext.unregisterReceiver(mReceiver); + } + } + /** * Runs an adb test with the provided configuration. * From 7dc919d0f347179536c1cbd0d6c3310a28a4ad32 Mon Sep 17 00:00:00 2001 From: Hui Yu Date: Mon, 7 Feb 2022 10:12:49 -0800 Subject: [PATCH 032/208] BG-FGS-start while-in-use permission restriction improvement. [This is a resbumit, previous fix ag/I0aca484e5a0dd051bbeac379d30b0fb4ecfa2da0 was reverted because the incorrect resetFgsRestrictionLocked() call] Foreground service started from background shall not have while-in-use access like location/camera/microphone. Previously we set mAllowWhileInUsePermissionInFgs only at service start by startService() or bindService() command. But after service start, the Service.startForeground() call may be some time later and at that time the caller may not be in the foreground any more. This CL will add further restriction on that. 1. If the first Service.startForeground() call is more than 10 seconds (can be configured by DeviceConfig key "fgs_start_foreground_timeout") after the Context.startService() call, check the service's app proc state and set mAllowWhileInUsePermissionInFgs again. 2. At Service.stopForeground() call, mAllowWhileInUsePermissionInFgs should be reset to false so FGS while-in-use permission is not allowed. 3. After Context.startForegroundService()(or Context.startService()) -> Service.startForeground() -> Service.stopForeground(), the second or more times Service.startForeground() is called, check the service's app proc state and set mAllowWhileInUsePermissionInFgs again. This CL is the backport of ag/Idc88f274c7a323d175d65bb47eca041772ae9bb7 from S branch. Bug: 183147114 Bug: 183204439 Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testStartForegroundTimeout Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testSecondStartForeground Change-Id: Ie8712b8efe85aa8a6769b811c85a29c4013e58b9 Merged-In: Idc88f274c7a323d175d65bb47eca041772ae9bb7 (cherry picked from commit d5abccff3c61b81aeb67d6fda10d9a27d3e326bd) Merged-In:Ie8712b8efe85aa8a6769b811c85a29c4013e58b9 --- .../com/android/server/am/ActiveServices.java | 107 ++++++++++++++---- .../server/am/ActivityManagerConstants.java | 20 ++++ .../com/android/server/am/ServiceRecord.java | 6 + 3 files changed, 112 insertions(+), 21 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index e4dd8756fe62..aa38fd1e6fc4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -734,11 +734,8 @@ ComponentName startServiceLocked(IApplicationThread caller, Intent service, Stri } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, - callingUid, service, r, allowBackgroundActivityStarts); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r, + allowBackgroundActivityStarts); return cmp; } @@ -1411,14 +1408,6 @@ private void setServiceForegroundInnerLocked(final ServiceRecord r, int id, + String.format("0x%08X", manifestType) + " in service element of manifest file"); } - // If the foreground service is not started from TOP process, do not allow it to - // have while-in-use location/camera/microphone access. - if (!r.mAllowWhileInUsePermissionInFgs) { - Slog.w(TAG, - "Foreground service started from background can not have " - + "location/camera/microphone access: service " - + r.shortInstanceName); - } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; @@ -1466,6 +1455,56 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { ignoreForeground = true; } + if (!ignoreForeground) { + if (r.mStartForegroundCount == 0) { + /* + If the service was started with startService(), not + startForegroundService(), and if startForeground() isn't called within + mFgsStartForegroundTimeoutMs, then we check the state of the app + (who owns the service, which is the app that called startForeground()) + again. If the app is in the foreground, or in any other cases where + FGS-starts are allowed, then we still allow the FGS to be started. + Otherwise, startForeground() would fail. + + If the service was started with startForegroundService(), then the service + must call startForeground() within a timeout anyway, so we don't need this + check. + */ + if (!r.fgRequired) { + final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime; + if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { + resetFgsRestrictionLocked(r); + setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, + r.appInfo.uid, r, false); + EventLog.writeEvent(0x534e4554, "183147114", + r.appInfo.uid, + "call setFgsRestrictionLocked again due to " + + "startForegroundTimeout"); + } + } + } else if (r.mStartForegroundCount >= 1) { + // The second or later time startForeground() is called after service is + // started. Check for app state again. + final long delayMs = SystemClock.elapsedRealtime() - + r.mLastSetFgsRestrictionTime; + if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { + setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, + r.appInfo.uid, r, false); + EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid, + "call setFgsRestrictionLocked for " + + (r.mStartForegroundCount + 1) + "th startForeground"); + } + } + // If the foreground service is not started from TOP process, do not allow it to + // have while-in-use location/camera/microphone access. + if (!r.mAllowWhileInUsePermissionInFgs) { + Slog.w(TAG, + "Foreground service started from background can not have " + + "location/camera/microphone access: service " + + r.shortInstanceName); + } + } + // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -1501,6 +1540,7 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { active.mNumActive++; } r.isForeground = true; + r.mStartForegroundCount++; if (!stopProcStatsOp) { ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1559,6 +1599,7 @@ && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { decActiveForegroundAppLocked(smap, r); } r.isForeground = false; + resetFgsRestrictionLocked(r); ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), @@ -2118,12 +2159,7 @@ public void run() { } } - if (!s.mAllowWhileInUsePermissionInFgs) { - s.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - callingPid, callingUid, - service, s, false); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false); if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -3419,7 +3455,7 @@ private final void bringDownServiceLocked(ServiceRecord r) { r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; - r.mAllowWhileInUsePermissionInFgs = false; + resetFgsRestrictionLocked(r); // Clear start entries. r.clearDeliveredStartsLocked(); @@ -4900,7 +4936,7 @@ private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw, * @return true if allow, false otherwise. */ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, - int callingPid, int callingUid, Intent intent, ServiceRecord r, + int callingPid, int callingUid, ServiceRecord r, boolean allowBackgroundActivityStarts) { // Is the background FGS start restriction turned on? if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { @@ -4982,4 +5018,33 @@ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage } return false; } + + boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid, + String callingPackage) { + return shouldAllowWhileInUsePermissionInFgsLocked( + callingPackage, callingPid, callingUid, null, false); + } + + /** + * In R, mAllowWhileInUsePermissionInFgs is to 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 r the service to start. + * @return true if allow, false otherwise. + */ + private void setFgsRestrictionLocked(String callingPackage, + int callingPid, int callingUid, ServiceRecord r, + boolean allowBackgroundActivityStarts) { + r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime(); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked( + callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); + } + } + + private void resetFgsRestrictionLocked(ServiceRecord r) { + r.mAllowWhileInUsePermissionInFgs = false; + r.mLastSetFgsRestrictionTime = 0; + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 7be843f17863..00d8208ea118 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -88,6 +88,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_PROCESS_START_ASYNC = "process_start_async"; static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time"; static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration"; + static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -121,6 +122,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_PROCESS_START_ASYNC = true; private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; + private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000; // Flag stored in the DeviceConfig API. @@ -273,6 +275,12 @@ final class ActivityManagerConstants extends ContentObserver { // this long. public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION; + /** + * When service started from background, before the timeout it can be promoted to FGS by calling + * Service.startForeground(). + */ + volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS; + // Indicates whether the activity starts logging is enabled. // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; @@ -421,6 +429,9 @@ public void onPropertiesChanged(Properties properties) { case KEY_MIN_ASSOC_LOG_DURATION: updateMinAssocLogDuration(); break; + case KEY_FGS_START_FOREGROUND_TIMEOUT: + updateFgsStartForegroundTimeout(); + break; default: break; } @@ -697,6 +708,13 @@ private void updateMinAssocLogDuration() { /* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION); } + private void updateFgsStartForegroundTimeout() { + mFgsStartForegroundTimeoutMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_FOREGROUND_TIMEOUT, + DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS); + } + void dump(PrintWriter pw) { pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) " + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":"); @@ -769,6 +787,8 @@ void dump(PrintWriter pw) { pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray())); pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("="); pw.println(MIN_ASSOC_LOG_DURATION); + pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("="); + pw.println(mFgsStartForegroundTimeoutMs); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 1b65dbac2294..0e628289a09f 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -142,6 +142,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; + // The number of times Service.startForeground() is called; + int mStartForegroundCount; + // Last time mAllowWhileInUsePermissionInFgs is set. + long mLastSetFgsRestrictionTime; // the most recent package that start/bind this service. String mRecentCallingPackage; @@ -406,6 +410,8 @@ void dump(PrintWriter pw, String prefix) { } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); + pw.print(prefix); pw.print("startForegroundCount="); + pw.println(mStartForegroundCount); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); if (delayed) { From 875c00e2542006fd062d68f0b5e7a12753d8d571 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Tue, 18 Jan 2022 18:29:52 +0800 Subject: [PATCH 033/208] [RESTRICT AUTOMERGE] Add hide-non-system-overlay flag for HarmfulAppWarningActivity A malicious application could overlay the activity. The overlay is able to be tapped through, which can trick the user into starting a harmful activity. The CL added the flag SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS for the activity to prevent the tapjacking/overlay attack. Bug: 205595291 Test: atest CtsHarmfulAppWarningHostTestCases Change-Id: Ia1a1ae0dc451e04bf5c31e3cb8cf30a0d8e32991 (cherry picked from commit a04b3666b8619e09e08646c6d5c529d016cbfb47) (cherry picked from commit 2c87a8a7cec276a9e4cf88e0ae410fd43ffb0b38) Merged-In:Ia1a1ae0dc451e04bf5c31e3cb8cf30a0d8e32991 --- .../com/android/internal/app/HarmfulAppWarningActivity.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java index ce2d229d41b3..33209e110123 100644 --- a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java +++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -27,6 +29,7 @@ import android.util.Log; import android.view.View; import android.widget.TextView; + import com.android.internal.R; /** @@ -48,6 +51,7 @@ public class HarmfulAppWarningActivity extends AlertActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); final Intent intent = getIntent(); mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT); From 3e9c7dffe82a6a4eeeb20f0eccde13374d30d365 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 11 Feb 2022 17:08:58 -0500 Subject: [PATCH 034/208] Filter notification APIs by user Specifically getActiveNotifications and getHistoricalNotifications Test: atest NotificationManagerServiceTest Bug: 214999128 Change-Id: I2eba0a592fa33ed25e1ac3919f1b2631e5db4258 Merged-In: I2eba0a592fa33ed25e1ac3919f1b2631e5db4258 (cherry picked from commit 3456555b7cfa84752b4dfa75191b7e87dc95b156) Merged-In: I2eba0a592fa33ed25e1ac3919f1b2631e5db4258 --- .../NotificationManagerService.java | 37 ++++++++++++----- .../server/notification/ArchiveTest.java | 40 ++++++++++++++++--- .../NotificationManagerServiceTest.java | 35 +++++++++++++++- 3 files changed, 96 insertions(+), 16 deletions(-) mode change 100644 => 100755 services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index eb91a663d18a..ddfe5b03d52a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -595,7 +595,14 @@ public Iterator> descendingIterator() { return mBuffer.descendingIterator(); } - public StatusBarNotification[] getArray(int count, boolean includeSnoozed) { + public StatusBarNotification[] getArray(UserManager um, int count, boolean includeSnoozed) { + ArrayList currentUsers = new ArrayList<>(); + currentUsers.add(UserHandle.USER_ALL); + Binder.withCleanCallingIdentity(() -> { + for (int user : um.getProfileIds(ActivityManager.getCurrentUser(), false)) { + currentUsers.add(user); + } + }); if (count == 0) count = mBufferSize; List a = new ArrayList(); Iterator> iter = descendingIterator(); @@ -603,8 +610,10 @@ public StatusBarNotification[] getArray(int count, boolean includeSnoozed) { while (iter.hasNext() && i < count) { Pair pair = iter.next(); if (pair.second != REASON_SNOOZED || includeSnoozed) { - i++; - a.add(pair.first); + if (currentUsers.contains(pair.first.getUserId())) { + i++; + a.add(pair.first); + } } } return a.toArray(new StatusBarNotification[a.size()]); @@ -3831,22 +3840,32 @@ public StatusBarNotification[] getActiveNotificationsWithAttribution(String call android.Manifest.permission.ACCESS_NOTIFICATIONS, "NotificationManagerService.getActiveNotifications"); - StatusBarNotification[] tmp = null; + ArrayList tmp = new ArrayList<>(); int uid = Binder.getCallingUid(); + ArrayList currentUsers = new ArrayList<>(); + currentUsers.add(UserHandle.USER_ALL); + Binder.withCleanCallingIdentity(() -> { + for (int user : mUm.getProfileIds(ActivityManager.getCurrentUser(), false)) { + currentUsers.add(user); + } + }); + // noteOp will check to make sure the callingPkg matches the uid if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg, callingAttributionTag, null) == AppOpsManager.MODE_ALLOWED) { synchronized (mNotificationLock) { - tmp = new StatusBarNotification[mNotificationList.size()]; final int N = mNotificationList.size(); - for (int i=0; i expected = new ArrayList<>(); @@ -75,13 +88,29 @@ public void testRecordAndRead() { mArchive.record(sbn, REASON_CANCEL); } - List actual = Arrays.asList(mArchive.getArray(SIZE, true)); + List actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true)); assertThat(actual).hasSize(expected.size()); for (StatusBarNotification sbn : actual) { assertThat(expected).contains(sbn.getKey()); } } + @Test + public void testCrossUser() { + mArchive.record(getNotification("pkg", 1, UserHandle.of(USER_SYSTEM)), REASON_CANCEL); + mArchive.record(getNotification("pkg", 2, UserHandle.of(USER_CURRENT)), REASON_CANCEL); + mArchive.record(getNotification("pkg", 3, UserHandle.of(USER_ALL)), REASON_CANCEL); + mArchive.record(getNotification("pkg", 4, UserHandle.of(USER_NULL)), REASON_CANCEL); + + List actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true)); + assertThat(actual).hasSize(3); + for (StatusBarNotification sbn : actual) { + if (sbn.getUserId() == USER_NULL) { + fail("leaked notification from wrong user"); + } + } + } + @Test public void testRecordAndRead_overLimit() { List expected = new ArrayList<>(); @@ -93,7 +122,8 @@ public void testRecordAndRead_overLimit() { } } - List actual = Arrays.asList(mArchive.getArray((SIZE * 2), true)); + List actual = Arrays.asList( + mArchive.getArray(mUm, (SIZE * 2), true)); assertThat(actual).hasSize(expected.size()); for (StatusBarNotification sbn : actual) { assertThat(expected).contains(sbn.getKey()); @@ -113,7 +143,7 @@ public void testDoesNotRecordIfHistoryDisabled() { } } - List actual = Arrays.asList(mArchive.getArray(SIZE, true)); + List actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true)); assertThat(actual).hasSize(expected.size()); for (StatusBarNotification sbn : actual) { assertThat(expected).contains(sbn.getKey()); @@ -134,7 +164,7 @@ public void testRemovesEntriesWhenHistoryDisabled() { } mArchive.updateHistoryEnabled(USER_CURRENT, false); - List actual = Arrays.asList(mArchive.getArray(SIZE, true)); + List actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true)); assertThat(actual).hasSize(expected.size()); for (StatusBarNotification sbn : actual) { assertThat(expected).contains(sbn.getKey()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java old mode 100644 new mode 100755 index c0ecbbadb695..6e8b1d91196c --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -466,6 +466,7 @@ public void setUp() throws Exception { when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG}); mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); + when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0}); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); @@ -6179,8 +6180,9 @@ public void testGrantInlineReplyUriPermission_acrossUsers() throws Exception { waitForIdle(); // A notification exists for the given record - StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); - assertEquals(1, notifsBefore.length); + List notifsBefore = + mBinderService.getAppActiveNotifications(PKG, nr.getSbn().getUserId()).getList(); + assertEquals(1, notifsBefore.size()); reset(mPackageManager); @@ -7098,4 +7100,33 @@ public void testCannotPostNonFgsWhenOverLimit() throws RemoteException { assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS + 1, mService.getNotificationRecordCount()); } + + @Test + public void testGetActiveNotification_filtersUsers() throws Exception { + when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0, 10}); + + NotificationRecord nr0 = + generateNotificationRecord(mTestNotificationChannel, 0); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag0", + nr0.getSbn().getId(), nr0.getSbn().getNotification(), nr0.getSbn().getUserId()); + + NotificationRecord nr10 = + generateNotificationRecord(mTestNotificationChannel, 10); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag10", + nr10.getSbn().getId(), nr10.getSbn().getNotification(), nr10.getSbn().getUserId()); + + NotificationRecord nr11 = + generateNotificationRecord(mTestNotificationChannel, 11); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag11", + nr11.getSbn().getId(), nr11.getSbn().getNotification(), nr11.getSbn().getUserId()); + waitForIdle(); + + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(2, notifs.length); + for (StatusBarNotification sbn : notifs) { + if (sbn.getUserId() == 11) { + fail("leaked data across users"); + } + } + } } From 5aa832c341b604b5a6bd908b25800a8b2cc12d40 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Tue, 8 Feb 2022 14:06:14 +0800 Subject: [PATCH 035/208] [RESTRICT AUTOMERGE] Do not resume activity if behind a translucent task The top-focusable activity resides in the RESUMED state while the app process is newly created and attached. The behavior may enable UI hijacking attacks against apps implementing authentication. This CL disallows the system to resume the activity for the case if it is not visible or is occluded by other translucent tasks. Bug: 211481342 Test: atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests Change-Id: I7903494cf928b5b5613700262b7c5fff10f3c5a0 (cherry picked from commit 43b8bcc01474bce480642acae2c554393c3bfb6a) Merged-In: I7903494cf928b5b5613700262b7c5fff10f3c5a0 --- .../server/wm/EnsureActivitiesVisibleHelper.java | 2 +- .../com/android/server/wm/RootWindowContainer.java | 3 ++- services/core/java/com/android/server/wm/Task.java | 11 +++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index aa76a07bc9c7..7b7fcc4e8f8b 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -93,7 +93,7 @@ void process(@Nullable ActivityRecord starting, int configChanges, boolean prese // activities are actually behind other fullscreen activities, but still required // to be visible (such as performing Recents animation). final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind - && mContiner.isTopActivityFocusable() + && mContiner.canBeResumed(starting) && (starting == null || !starting.isDescendantOf(mContiner)); final PooledConsumer f = PooledLambda.obtainConsumer( diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b1d774af7f36..f173c0b1caf9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1990,7 +1990,8 @@ private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r, try { if (mStackSupervisor.realStartActivityLocked(r, app, - top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) { + top == r && r.getTask().canBeResumed(r) /*andResume*/, + true /*checkConfig*/)) { mTmpBoolean = true; } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index b19b8c1ce10b..2da9c8db9ff8 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3660,6 +3660,17 @@ boolean shouldBeVisible(ActivityRecord starting) { return getVisibility(starting) != STACK_VISIBILITY_INVISIBLE; } + /** + * Returns {@code true} is the activity in this Task can be resumed. + * + * @param starting The currently starting activity or {@code null} if there is none. + */ + boolean canBeResumed(@Nullable ActivityRecord starting) { + // No need to resume activity in Task that is not visible. + return isTopActivityFocusable() + && getVisibility(starting) == STACK_VISIBILITY_VISIBLE; + } + /** * Returns true if the task should be visible. * From c62a96ca3c573dcfc5c31abcb32b42ac70be24f2 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Fri, 1 Oct 2021 11:03:16 -0400 Subject: [PATCH 036/208] [DO NOT MERGE] Keyguard - Treat messsages to lock with priority When switching users and attempting to lock the device, the sysui main thread becomes overwhelmed with events, creating a significant lag between the time a message is posted and processed on the main thread. This can be dangerous when these events are critical for security, such as calls coming from PhoneWindowManager#lockNow() that call KeyguardViewMediator#doKeyguardTimeout(). On older devices with slower CPUs and less memory, the delay in processing can be significant (15 - 30s). The result of not prioritizing these events leads to a window of time where a guest user can switch back to the owner, and gain access to the owner's homescreen without needing to unlock the device with the owner's credentials. As a mitigation, prioritize two events originating in two specific methods to make sure the device locks as soon as possible as well as have the system server preemptively update its local cache. Bug: 151095871 Test: Very manual race condition - follow steps listed in bug Change-Id: I7585a0a5eeb308e0e32a4f77f581556d883b5cda Merged-In: I7585a0a5eeb308e0e32a4f77f581556d883b5cda (cherry picked from commit 28c53ab8bca26af58b45625c1ebba8b9051c107d) (cherry picked from commit 21e48cf26bbcd7f13a623e22a25dbc0340c4e4eb) Merged-In: I7585a0a5eeb308e0e32a4f77f581556d883b5cda --- .../internal/policy/IKeyguardStateCallback.aidl | 2 +- .../systemui/keyguard/KeyguardViewMediator.java | 16 +++++++++++----- .../policy/keyguard/KeyguardServiceWrapper.java | 6 ++++++ .../policy/keyguard/KeyguardStateMonitor.java | 8 +++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl index 8e454db4cb04..a8003a1169e9 100644 --- a/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl +++ b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl @@ -16,7 +16,7 @@ package com.android.internal.policy; interface IKeyguardStateCallback { - void onShowingStateChanged(boolean showing); + void onShowingStateChanged(boolean showing, int userId); void onSimSecureStateChanged(boolean simSecure); void onInputRestrictedStateChanged(boolean inputRestricted); void onTrustedChanged(boolean trusted); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 941bd3fdf274..bef47fe6a374 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1325,7 +1325,9 @@ private void handleSetOccluded(boolean isOccluded, boolean animate) { public void doKeyguardTimeout(Bundle options) { mHandler.removeMessages(KEYGUARD_TIMEOUT); Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT, options); - mHandler.sendMessage(msg); + // Treat these messages with priority - A call to timeout means the device should lock + // as soon as possible and not wait for other messages on the thread to process first. + mHandler.sendMessageAtFrontOfQueue(msg); } /** @@ -1539,12 +1541,15 @@ private void notifyScreenTurnedOff() { * @see #handleShow */ private void showLocked(Bundle options) { - Trace.beginSection("KeyguardViewMediator#showLocked aqcuiring mShowKeyguardWakeLock"); + Trace.beginSection("KeyguardViewMediator#showLocked acquiring mShowKeyguardWakeLock"); if (DEBUG) Log.d(TAG, "showLocked"); // ensure we stay awake until we are finished displaying the keyguard mShowKeyguardWakeLock.acquire(); Message msg = mHandler.obtainMessage(SHOW, options); - mHandler.sendMessage(msg); + // Treat these messages with priority - This call can originate from #doKeyguardTimeout, + // meaning the device should lock as soon as possible and not wait for other messages on + // the thread to process first. + mHandler.sendMessageAtFrontOfQueue(msg); Trace.endSection(); } @@ -1706,6 +1711,7 @@ public void handleMessage(Message msg) { case KEYGUARD_TIMEOUT: synchronized (KeyguardViewMediator.this) { doKeyguardLocked((Bundle) msg.obj); + notifyDefaultDisplayCallbacks(mShowing); } break; case DISMISS: @@ -2335,7 +2341,7 @@ private void notifyDefaultDisplayCallbacks(boolean showing) { for (int i = size - 1; i >= 0; i--) { IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i); try { - callback.onShowingStateChanged(showing); + callback.onShowingStateChanged(showing, KeyguardUpdateMonitor.getCurrentUser()); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onShowingStateChanged", e); if (e instanceof DeadObjectException) { @@ -2384,7 +2390,7 @@ public void addStateMonitorCallback(IKeyguardStateCallback callback) { mKeyguardStateCallbacks.add(callback); try { callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure()); - callback.onShowingStateChanged(mShowing); + callback.onShowingStateChanged(mShowing, KeyguardUpdateMonitor.getCurrentUser()); callback.onInputRestrictedStateChanged(mInputRestricted); callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust( KeyguardUpdateMonitor.getCurrentUser())); diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java index 4e848686254a..cf5c587e0494 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java @@ -192,6 +192,12 @@ public void onSystemReady() { @Override // Binder interface public void doKeyguardTimeout(Bundle options) { + int userId = mKeyguardStateMonitor.getCurrentUser(); + if (mKeyguardStateMonitor.isSecure(userId)) { + // Preemptively inform the cache that the keyguard will soon be showing, as calls to + // doKeyguardTimeout are a signal to lock the device as soon as possible. + mKeyguardStateMonitor.onShowingStateChanged(true, userId); + } try { mService.doKeyguardTimeout(options); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index e56a4532ef53..d34863f22ea5 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -92,8 +92,14 @@ public boolean hasLockscreenWallpaper() { return mHasLockscreenWallpaper; } + public int getCurrentUser() { + return mCurrentUserId; + } + @Override // Binder interface - public void onShowingStateChanged(boolean showing) { + public void onShowingStateChanged(boolean showing, int userId) { + if (userId != mCurrentUserId) return; + mIsShowing = showing; mCallback.onShowingChanged(); From 44b0cbb10949db6950e790495f93c7e3ecb3edb7 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Thu, 3 Mar 2022 18:24:37 +0000 Subject: [PATCH 037/208] Verify caller before auto granting slice permission Currently SliceManagerService#checkSlicePermission does not verify the caller's identity. This leads to a security vulnerability because checkSlicePermission does more than checking the permission as opposed to simply return a boolean value -- it additionally grants slice access under a certain condition. A malicious app can spoof the calling package to acquire slice access. This CL verifies the caller before granting slice access. Bug: 208232850, 179699767 Test: manual Change-Id: I2539c9ff5ea977c91bb58185c95280b4d533a520 Merged-In: I2539c9ff5ea977c91bb58185c95280b4d533a520 (cherry picked from commit 5bd2196c537ae42a5c1626bdc23c3c6db41fb97f) (cherry picked from commit 3c92d74d7d74e1d781ae1b071da97b3b2cbc6be9) Merged-In: I2539c9ff5ea977c91bb58185c95280b4d533a520 --- .../core/java/com/android/server/slice/SliceManagerService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index c49720faf379..94201cf01ec9 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -242,6 +242,8 @@ public int checkSlicePermission(Uri uri, String callingPkg, String pkg, int pid, if (autoGrantPermissions != null && callingPkg != null) { // Need to own the Uri to call in with permissions to grant. enforceOwner(callingPkg, uri, userId); + // b/208232850: Needs to verify caller before granting slice access + verifyCaller(callingPkg); for (String perm : autoGrantPermissions) { if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) { int providerUser = ContentProvider.getUserIdFromUri(uri, userId); From 2739efb66e25e9c4dcb88b7742b0d1a9aa6f6e0d Mon Sep 17 00:00:00 2001 From: Alex Buynytskyy Date: Thu, 24 Feb 2022 21:40:13 -0800 Subject: [PATCH 038/208] Always restart apps if base.apk gets updated. Bug: 219044664 Fixes: 219044664 Test: atest PackageManagerShellCommandTest Change-Id: I27a0c5009b2d5f1ea51618b9acfa1e6ccee71296 Merged-In: I27a0c5009b2d5f1ea51618b9acfa1e6ccee71296 (cherry picked from commit 52a4337a4790350e8270b0712d9977159c07e096) Merged-In: I27a0c5009b2d5f1ea51618b9acfa1e6ccee71296 --- .../android/content/pm/IPackageInstallerSession.aidl | 1 + core/java/android/content/pm/PackageInstaller.java | 12 ++++++++++++ .../android/server/pm/PackageInstallerSession.java | 11 +++++++++++ 3 files changed, 24 insertions(+) diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index fc20263fe00a..de76a68e1d92 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -51,4 +51,5 @@ interface IPackageInstallerSession { int getParentSessionId(); boolean isStaged(); + int getInstallFlags(); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 191c4655c708..d56f19909dc7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1358,6 +1358,18 @@ public boolean isStaged() { } } + /** + * @return Session's {@link SessionParams#installFlags}. + * @hide + */ + public int getInstallFlags() { + try { + return mSession.getInstallFlags(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * @return the session ID of the multi-package session that this belongs to or * {@link SessionInfo#INVALID_ID} if it does not belong to a multi-package session. diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index a253b509abb9..016ee328fbee 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -119,6 +119,7 @@ import android.system.StructStat; import android.text.TextUtils; import android.util.ArraySet; +import android.util.EventLog; import android.util.ExceptionUtils; import android.util.MathUtils; import android.util.Slog; @@ -2223,6 +2224,11 @@ private void validateApkInstallLocked() throws PackageManagerException { resolveInheritedFile(baseDexMetadataFile); } baseApk = existingBase; + } else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { + EventLog.writeEvent(0x534e4554, "219044664"); + + // Installing base.apk. Make sure the app is restarted. + params.setDontKillApp(false); } // Inherit splits if not overridden @@ -2676,6 +2682,11 @@ public boolean isStaged() { return params.isStaged; } + @Override + public int getInstallFlags() { + return params.installFlags; + } + @Override public DataLoaderParamsParcel getDataLoaderParams() { mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); From b5214c879863af1cf2c0e0960b03e3fee927c979 Mon Sep 17 00:00:00 2001 From: David Christie Date: Fri, 11 Mar 2022 01:13:31 +0000 Subject: [PATCH 039/208] Update GeofenceHardwareRequestParcelable to match parcel/unparcel format. Test: manual Bug: 216631962 Change-Id: I3d6d1be9d6c312fe0bf98f600ff8fc9c617f8ec3 (cherry picked from commit ba3acb3e53c2f299a6326434ee19fe5e18ce8a30) Merged-In: I3d6d1be9d6c312fe0bf98f600ff8fc9c617f8ec3 --- .../location/GeofenceHardwareRequestParcelable.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java index df13ade2bf5e..bd25b8f2ad88 100644 --- a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java +++ b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java @@ -16,9 +16,9 @@ package android.hardware.location; +import android.os.BadParcelableException; import android.os.Parcel; import android.os.Parcelable; -import android.util.Log; /** * Geofence Hardware Request used for internal location services communication. @@ -139,11 +139,8 @@ public String toString() { @Override public GeofenceHardwareRequestParcelable createFromParcel(Parcel parcel) { int geofenceType = parcel.readInt(); - if(geofenceType != GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) { - Log.e( - "GeofenceHardwareRequest", - String.format("Invalid Geofence type: %d", geofenceType)); - return null; + if (geofenceType != GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) { + throw new BadParcelableException("Invalid Geofence type: " + geofenceType); } GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence( From 59c543ac03bdcb8d2026459396b91e54e9f24abb Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 1 Mar 2022 10:30:27 -0500 Subject: [PATCH 040/208] DO NOT MERGE Add an OEM configurable limit for zen rules Test: ZenModeHelperTest Bug: 220735360 Change-Id: I3da105951af90007bf48dc6cf00aed3e28778b36 Merged-In: I3da105951af90007bf48dc6cf00aed3e28778b36 (cherry picked from commit 1db5d40205929f253f1d2bbf541f428020b3b28e) Merged-In: I3da105951af90007bf48dc6cf00aed3e28778b36 --- .../server/notification/ZenModeHelper.java | 5 +- .../notification/ZenModeHelperTest.java | 49 ++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4931d3f3e95c..49a016cb45b0 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -102,6 +102,7 @@ public class ZenModeHelper { // The amount of time rules instances can exist without their owning app being installed. private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72; + static final int RULE_LIMIT_PER_PACKAGE = 100; private final Context mContext; private final H mHandler; @@ -319,10 +320,10 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reas int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner()) + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity()) + 1; - if (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount) { + if (newRuleInstanceCount > RULE_LIMIT_PER_PACKAGE + || (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount)) { throw new IllegalArgumentException("Rule instance limit exceeded"); } - } ZenModeConfig newConfig; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 3c7206fee9d1..c98745e9041d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -41,9 +41,11 @@ import static com.android.os.AtomsProto.DNDModeProto.ID_FIELD_NUMBER; import static com.android.os.AtomsProto.DNDModeProto.UID_FIELD_NUMBER; import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER; +import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.fail; @@ -70,7 +72,9 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.media.AudioAttributes; @@ -102,6 +106,8 @@ import com.android.server.UiServiceTestCase; import com.android.server.notification.ManagedServices.UserProfiles; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -131,9 +137,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE"; private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE"; private static final int ZEN_MODE_FOR_TESTING = 99; + private static final String CUSTOM_PKG_NAME = "not.android"; + private static final int CUSTOM_PKG_UID = 1; ConditionProviders mConditionProviders; @Mock NotificationManager mNotificationManager; + @Mock PackageManager mPackageManager; private Resources mResources; private TestableLooper mTestableLooper; private ZenModeHelper mZenModeHelperSpy; @@ -143,7 +152,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory; @Before - public void setUp() { + public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); @@ -166,6 +175,16 @@ public void setUp() { mConditionProviders.addSystemProvider(new CountdownConditionProvider()); mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(), mConditionProviders, mStatsEventBuilderFactory)); + + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + when(mPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt())).thenReturn( + ImmutableList.of(ri)); + when(mPackageManager.getPackageUidAsUser(eq(CUSTOM_PKG_NAME), anyInt())) + .thenReturn(CUSTOM_PKG_UID); + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( + new String[] {getContext().getPackageName()}); + mZenModeHelperSpy.mPm = mPackageManager; } private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException { @@ -1556,6 +1575,34 @@ public void testAddAutomaticZenRule() { assertEquals(zenRule.getName(), ruleInConfig.name); } + @Test + public void testAddAutomaticZenRule_beyondSystemLimit() { + for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { + ScheduleInfo si = new ScheduleInfo(); + si.startHour = i; + AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, + null, + new ComponentName("android", "ScheduleConditionProvider"), + ZenModeConfig.toScheduleConditionId(si), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + assertNotNull(id); + } + try { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + null, + new ComponentName("android", "ScheduleConditionProvider"), + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + fail("allowed too many rules to be created"); + } catch (IllegalArgumentException e) { + // yay + } + } + private void setupZenConfig() { mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConfig.allowAlarms = false; From 91c6a72ca60904a16d7eeaf94cb538a7c9fd3ecb Mon Sep 17 00:00:00 2001 From: Ayush Sharma Date: Wed, 16 Mar 2022 10:32:23 +0000 Subject: [PATCH 041/208] Fix security hole in GateKeeperResponse GateKeeperResponse has inconsistent writeToParcel() and createFromParcel() methods, making it possible for a malicious app to create a Bundle that changes contents after reserialization. Such Bundles can be used to execute Intents with system privileges. We fixed related issues previously for GateKeeperResponse class, but one of the case was remaining when payload is byte array of size 0, Fixing this case now. Bug: 220303465 Test: With the POC provided in the bug. Change-Id: Ida28d611edd674e76ed39dd8037f52abcba82586 Merged-In: Ida28d611edd674e76ed39dd8037f52abcba82586 (cherry picked from commit 46653a91c30245ca29d41d69174813979a910496) Change-Id: I486348c7a01c6f59c952b20fb4a36429fff22958 (cherry picked from commit 658c53c47c0d1b6a74d3c0a72372aaaba16c2516) Merged-In: I486348c7a01c6f59c952b20fb4a36429fff22958 --- core/java/android/service/gatekeeper/GateKeeperResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/service/gatekeeper/GateKeeperResponse.java b/core/java/android/service/gatekeeper/GateKeeperResponse.java index 7ed733cb4f4c..9d648a6995fb 100644 --- a/core/java/android/service/gatekeeper/GateKeeperResponse.java +++ b/core/java/android/service/gatekeeper/GateKeeperResponse.java @@ -105,7 +105,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTimeout); } else if (mResponseCode == RESPONSE_OK) { dest.writeInt(mShouldReEnroll ? 1 : 0); - if (mPayload != null) { + if (mPayload != null && mPayload.length > 0) { dest.writeInt(mPayload.length); dest.writeByteArray(mPayload); } else { From 4fa21835843df0fcbec9b8f5efba4d860ededdd8 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 25 Mar 2022 18:24:39 +0000 Subject: [PATCH 042/208] Prevent non-admin users from deleting system apps. This addresses a security issue where the guest user can remove updates for system apps. With this CL, attempts to uninstall/downgrade system apps will fail if attempted by a non-admin user. This is a backport of ag/17352264. Bug: 170646036 Test: manual, try uninstalling system app update as guest Change-Id: I79c3bf303e729e00d8fb12c40330bc10c5ffec6e Merged-In: I4e959e296cca9bbdfc8fccc5e5e0e654ca524165 (cherry picked from commit 6c870e157994519094e9e50ddf93e57a26779e22) Merged-In: I79c3bf303e729e00d8fb12c40330bc10c5ffec6e --- .../com/android/server/pm/PackageManagerService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4b119598ad3a..71e665122167 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18653,6 +18653,16 @@ int deletePackageX(String packageName, long versionCode, int userId, int deleteF return PackageManager.DELETE_FAILED_INTERNAL_ERROR; } + if (isSystemApp(uninstalledPs)) { + UserInfo userInfo = mUserManager.getUserInfo(userId); + if (userInfo == null || !userInfo.isAdmin()) { + Slog.w(TAG, "Not removing package " + packageName + + " as only admin user may downgrade system apps"); + EventLog.writeEvent(0x534e4554, "170646036", -1, packageName); + return PackageManager.DELETE_FAILED_USER_RESTRICTED; + } + } + disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName); // Save the enabled state before we delete the package. When deleting a stub // application we always set the enabled state to 'disabled'. From f84107b778412806c8e5920a3c8e33bc7cdd366b Mon Sep 17 00:00:00 2001 From: JW Wang Date: Thu, 31 Mar 2022 10:06:06 +0800 Subject: [PATCH 043/208] Fix NPE NPE happens when there is an orphaned session which we've tried to prevent in all cases. Log an error message if this situation happens. Bug: 227342978 Test: atest CtsRootPackageInstallerHostTestCases Change-Id: Ia21323926bd9db1a6f05461904deb45b4c3dd0bc (cherry picked from commit 07e31dfb1efabc8110d64819f26a06e12a35e020) Merged-In: Ia21323926bd9db1a6f05461904deb45b4c3dd0bc (cherry picked from commit e58049a3ea2c056b999c281c7031f9e16e42f809) Merged-In: Ia21323926bd9db1a6f05461904deb45b4c3dd0bc --- .../java/com/android/server/pm/PackageInstallerService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 2439b3722d75..3aa270790bd1 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -346,7 +346,10 @@ public void freeStageDirs(String volumeUuid) { // Their staging dirs will be removed too PackageInstallerSession root = !session.hasParentSessionId() ? session : mSessions.get(session.getParentSessionId()); - if (!root.isDestroyed()) { + if (root == null) { + Slog.e(TAG, "freeStageDirs: found an orphaned session: " + + session.sessionId + " parent=" + session.getParentSessionId()); + } else if (!root.isDestroyed()) { root.abandon(); } } else { From f6454b3bdce3b8195ed9d9dee39ca2cbdbc72297 Mon Sep 17 00:00:00 2001 From: Jonathan Scott Date: Tue, 5 Apr 2022 18:47:56 +0000 Subject: [PATCH 044/208] [rvc] RESTRICT AUTOMERGE Add finalizeWorkProfileProvisioning. Test: atest android.devicepolicy.cts.DevicePolicyManagerTest Bug: 210469972 Change-Id: I2de99f9ccd8b27ffdc2562fa451f132e73d54317 (cherry picked from commit bde28d286e28b551dc9b11d882ad89120ac83107) Merged-In: I2de99f9ccd8b27ffdc2562fa451f132e73d54317 --- .../app/admin/DevicePolicyManager.java | 22 +++++++++++++ .../app/admin/IDevicePolicyManager.aidl | 3 ++ core/res/AndroidManifest.xml | 1 + .../BaseIDevicePolicyManager.java | 7 ++++ .../DevicePolicyManagerService.java | 33 +++++++++++++++++++ 5 files changed, 66 insertions(+) diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ecd0e65b8921..c2f00215dd3d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -19,6 +19,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest.permission; +import android.accounts.Account; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.IntDef; @@ -152,6 +153,27 @@ public DevicePolicyManager(Context context, IDevicePolicyManager service) { this(context, service, false); } + /** + * Called when a managed profile has been provisioned. + * + * @throws SecurityException if the caller does not hold + * {@link android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public void finalizeWorkProfileProvisioning( + @NonNull UserHandle managedProfileUser, @Nullable Account migratedAccount) { + Objects.requireNonNull(managedProfileUser, "managedProfileUser can't be null"); + if (mService == null) { + throw new IllegalStateException("Could not find DevicePolicyManagerService"); + } + try { + mService.finalizeWorkProfileProvisioning(managedProfileUser, migratedAccount); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ @VisibleForTesting protected DevicePolicyManager(Context context, IDevicePolicyManager service, diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 51ca730138e8..fc8c7a15c58c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -17,6 +17,7 @@ package android.app.admin; +import android.accounts.Account; import android.app.admin.NetworkEvent; import android.app.IApplicationThread; import android.app.IServiceConnection; @@ -91,6 +92,8 @@ interface IDevicePolicyManager { int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); + void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount); + void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num, boolean parent); int getMaximumFailedPasswordsForWipe(in ComponentName admin, int userHandle, boolean parent); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9aa9482f67a8..ea1ea9f9ed98 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -96,6 +96,7 @@ + diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index eff222a2051a..900d3e8898ac 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -15,8 +15,10 @@ */ package com.android.server.devicepolicy; +import android.accounts.Account; import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; +import android.os.UserHandle; import com.android.server.SystemService; @@ -86,4 +88,9 @@ public long getManagedProfileMaximumTimeOff(ComponentName admin) { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } + + public void finalizeWorkProfileProvisioning( + UserHandle managedProfileUser, Account migratedAccount) { + + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 118350f4e9fd..d9baa4131d42 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -23,6 +23,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE; +import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY; import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE; @@ -50,6 +51,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS; import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT; import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION; @@ -10680,6 +10682,37 @@ private void maybeSendAdminEnabledBroadcastLocked(int userHandle) { } } + @Override + public void finalizeWorkProfileProvisioning(UserHandle managedProfileUser, + Account migratedAccount) { + if (mContext.checkCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Calling identity is not authorized"); + } + + if (!isManagedProfile(managedProfileUser.getIdentifier())) { + throw new IllegalStateException("Given user is not a managed profile"); + } + ComponentName profileOwnerComponent = + mOwners.getProfileOwnerComponent(managedProfileUser.getIdentifier()); + if (profileOwnerComponent == null) { + throw new IllegalStateException("There is no profile owner on the given profile"); + } + Intent primaryProfileSuccessIntent = new Intent(ACTION_MANAGED_PROFILE_PROVISIONED); + primaryProfileSuccessIntent.setPackage(profileOwnerComponent.getPackageName()); + primaryProfileSuccessIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES + | Intent.FLAG_RECEIVER_FOREGROUND); + primaryProfileSuccessIntent.putExtra(Intent.EXTRA_USER, managedProfileUser); + + if (migratedAccount != null) { + primaryProfileSuccessIntent.putExtra(EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, + migratedAccount); + } + + mContext.sendBroadcastAsUser(primaryProfileSuccessIntent, + UserHandle.of(getProfileParentId(managedProfileUser.getIdentifier()))); + } + @Override public UserHandle createAndManageUser(ComponentName admin, String name, ComponentName profileOwner, PersistableBundle adminExtras, int flags) { From faf30a25db5b1884e4a0ea113fb59a517aca9aef Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Mon, 31 Jan 2022 20:31:42 +0000 Subject: [PATCH 045/208] limit TelecomManager#registerPhoneAccount to 10; api doc update bug: 209814693 Bug: 217934478 Test: CTS Change-Id: I8e4425a4e7de716f86b1f1f56ea605d93f357a57 Merged-In: I8e4425a4e7de716f86b1f1f56ea605d93f357a57 (cherry picked from commit f0f67b5a319efedbf8693b436a641fa65bc2d8be) Merged-In: I8e4425a4e7de716f86b1f1f56ea605d93f357a57 --- telecomm/java/android/telecom/TelecomManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 5bcf3f3158d1..c160023ca944 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1330,9 +1330,14 @@ public List getAllPhoneAccountHandles() { * when placing calls. The user may still need to enable the {@link PhoneAccount} within * the phone app settings before the account is usable. *

+ * Note: Each package is limited to 10 {@link PhoneAccount} registrations. + *

* A {@link SecurityException} will be thrown if an app tries to register a * {@link PhoneAccountHandle} where the package name specified within * {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app. + *

+ * A {@link IllegalArgumentException} will be thrown if an app tries to register a + * {@link PhoneAccount} when the upper bound limit, 10, has already been reached. * * @param account The complete {@link PhoneAccount}. */ From 7d08d5473f0a90b1b4288eef550797b09a4ce72e Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Mon, 28 Feb 2022 18:02:27 +0900 Subject: [PATCH 046/208] Disallow PAP authentication when MPPE is requested MPPE cannot work if PAP is used as authentication, so it is not useful to allow PAP authentication when MPPE is enforced: establishing the tunnel would fail anyway with "MPPE required, but MS-CHAP[v2] auth not performed". Also users enforcing MPPE may assume that this means PAP will not be used for authentication, so without this change MPPE enforcement gives a false sense of security, as PAP uses plain-text credentials. Bug: 201660636 Test: atest VpnTest Merged-In: Ie318d45fe44294e97cf38da7f1834cf014cb4417 Change-Id: Ie318d45fe44294e97cf38da7f1834cf014cb4417 (cherry picked from commit 997a4a39268b4f3af7ccc388269b5eb1972d3624) (cherry picked from commit 4f319df8ff5a4b9f2bc62cb17df972e40b57fc81) Merged-In: Ie318d45fe44294e97cf38da7f1834cf014cb4417 --- .../core/java/com/android/server/connectivity/Vpn.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 9edb0e440baa..8f9b5ca26e1b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2157,6 +2157,13 @@ public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore, "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", (profile.mppe ? "+mppe" : "nomppe"), }; + if (profile.mppe) { + // Disallow PAP authentication when MPPE is requested, as MPPE cannot work + // with PAP anyway, and users may not expect PAP (plain text) to be used when + // MPPE was requested. + mtpd = Arrays.copyOf(mtpd, mtpd.length + 1); + mtpd[mtpd.length - 1] = "-pap"; + } break; case VpnProfile.TYPE_L2TP_IPSEC_PSK: case VpnProfile.TYPE_L2TP_IPSEC_RSA: From 9f6eba1abd4bded9235a6375fc2c64e90063e231 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 13 Aug 2021 13:37:55 -0700 Subject: [PATCH 047/208] [RESTRICT AUTOMERGE] StorageManagerService: don't ignore failures to prepare user storage We must never leave directories unencrypted. Bug: 164488924 Bug: 224585613 Change-Id: I9a38ab5cca1ae9c9ebff81fca04615fd83ebe4b2 (cherry picked from commit 50946dd15fd14cbf92b5c7e32ac7a0f088b8b302) Merged-In: I9a38ab5cca1ae9c9ebff81fca04615fd83ebe4b2 (cherry picked from commit ecffe3ecbf4cb01055bd2f852d95396f2475fc01) Merged-In: I9a38ab5cca1ae9c9ebff81fca04615fd83ebe4b2 --- .../core/java/com/android/server/StorageManagerService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 46538d0207f6..8f0964663dab 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3410,8 +3410,12 @@ private void prepareUserStorageInternal(String volumeUuid, int userId, int seria mInstaller.tryMountDataMirror(volumeUuid); } } - } catch (Exception e) { + } catch (RemoteException | Installer.InstallerException e) { Slog.wtf(TAG, e); + // Make sure to re-throw this exception; we must not ignore failure + // to prepare the user storage as it could indicate that encryption + // wasn't successfully set up. + throw new RuntimeException(e); } } From 76488e74dd83b996f3563e8d0a94e99d6c3c9740 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 24 Jan 2022 20:33:11 +0000 Subject: [PATCH 048/208] [RESTRICT AUTOMERGE] UserDataPreparer: reboot to recovery if preparing user storage fails StorageManager.prepareUserStorage() can throw an exception if a directory cannot be encrypted, for example due to already being nonempty. In this case, usage of the directory must not be allowed to proceed. UserDataPreparer currently handles this by deleting the user's directories, but the error is still ultimately suppressed and starting the user is still allowed to proceed. The correct behavior in this case is to reboot into recovery to ask the user to factory reset the device. This is already what happens when 'init' fails to encrypt a directory with the system DE policy. However, this was overlooked for the user directories. Start doing this. Bug: 164488924 Bug: 224585613 Change-Id: Ib5e91d2510b25780d7a161b91b5cee2f6f7a2e54 (cherry picked from commit 5256365e65882b81509ec2f6b9dfe2dcf0025254) Merged-In: Ib5e91d2510b25780d7a161b91b5cee2f6f7a2e54 (cherry picked from commit 69c3ce70c6dcabf57219d338af86e569ea672ef5) Merged-In: Ib5e91d2510b25780d7a161b91b5cee2f6f7a2e54 --- .../core/java/com/android/server/pm/UserDataPreparer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 045a295da965..504769064808 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -22,6 +22,7 @@ import android.content.pm.UserInfo; import android.os.Environment; import android.os.FileUtils; +import android.os.RecoverySystem; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.os.SystemProperties; @@ -115,6 +116,13 @@ private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, in // Try one last time; if we fail again we're really in trouble prepareUserDataLI(volumeUuid, userId, userSerial, flags | StorageManager.FLAG_STORAGE_DE, false); + } else { + try { + Log.e(TAG, "prepareUserData failed", e); + RecoverySystem.rebootPromptAndWipeUserData(mContext, "prepareUserData failed"); + } catch (IOException e2) { + throw new RuntimeException("error rebooting into recovery", e2); + } } } } From ac5d80fa9be8e42218f84de3ff8ed823cdf296bb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 4 Mar 2022 00:07:29 +0000 Subject: [PATCH 049/208] [RESTRICT AUTOMERGE] UserDataPreparer: reboot to recovery for system user only With the next CL, old devices might contain a combination of old users with prepareUserStorage error checking disabled and new users with prepareUserStorage error checking enabled. Factory resetting the whole device when any user fails to prepare may be too aggressive. Also, UserDataPreparer already destroys the affected user's storage when it fails to prepare, which seems to be fairly effective at breaking things for that user (absent proper error handling by upper layers). Therefore, let's only factory reset the device if the failing user is the system user. Bug: 164488924 Bug: 224585613 Change-Id: Ia1db01ab4ec6b3b17d725f391c3500d92aa00f97 (cherry picked from commit 4c76da76c9831266e4e63c0618150bed10a929a7) Merged-In: Ia1db01ab4ec6b3b17d725f391c3500d92aa00f97 (cherry picked from commit ecf569bd1623231984e9ec9823edb82f52d7846a) Merged-In: Ia1db01ab4ec6b3b17d725f391c3500d92aa00f97 --- .../core/java/com/android/server/pm/UserDataPreparer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 504769064808..95482d7c7f1a 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -118,8 +118,11 @@ private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, in flags | StorageManager.FLAG_STORAGE_DE, false); } else { try { - Log.e(TAG, "prepareUserData failed", e); - RecoverySystem.rebootPromptAndWipeUserData(mContext, "prepareUserData failed"); + Log.wtf(TAG, "prepareUserData failed for user " + userId, e); + if (userId == UserHandle.USER_SYSTEM) { + RecoverySystem.rebootPromptAndWipeUserData(mContext, + "prepareUserData failed for system user"); + } } catch (IOException e2) { throw new RuntimeException("error rebooting into recovery", e2); } From 905f7f797f3008001ce604afd7d51dcd600cf398 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 4 Mar 2022 00:07:43 +0000 Subject: [PATCH 050/208] [RESTRICT AUTOMERGE] Ignore errors preparing user storage for existing users Unfortunately we can't rule out the existence of devices where the user storage wasn't properly prepared, due to StorageManagerService previously ignoring errors from mVold.prepareUserStorage, combined with OEMs potentially creating files in per-user directories too early. And forcing these broken devices to be factory reset upon taking an OTA is not currently considered to be acceptable. One option is to only check for prepareUserStorage errors on devices that launched with T or later. However, this is a serious issue and it would be strongly preferable to do more than that. Therefore, this CL makes it so that errors are checked for all new users, rather than all new devices. A field ignorePrepareStorageErrors is added to the user record; it is only ever set to true implicitly, when reading a user record from disk that lacks this field. This field is used by StorageManagerService to decide whether to check for errors. Bug: 164488924 Bug: 224585613 Test: Intentionally made a device affected by this issue by reverting the CLs that introduced the error checks, and changing vold to inject an error into prepareUserStorage. Then, flashed a build with this CL without wiping userdata. The device still boots, as expected, and the log shows that the error was intentionally ignored. Tested that if a second user is added, the error is *not* ignored and the second user's storage is destroyed before it can be used. Finally, wiped the device and verified that it won't boot up anymore, as expected since error checking is enabled for the system user in that case. Change-Id: I9bdd1a4bf5b14542adb901f264a91d489115c89b (cherry picked from commit 60d8318c47b7b659716d71243d087b34ab327f64) Merged-In: I9bdd1a4bf5b14542adb901f264a91d489115c89b (cherry picked from commit e03e987337accde646e4e86c1fdfe02c0d78d743) Merged-In: I9bdd1a4bf5b14542adb901f264a91d489115c89b --- .../java/android/os/UserManagerInternal.java | 8 ++++ .../android/server/StorageManagerService.java | 11 ++++- .../android/server/pm/UserManagerService.java | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java index 38983488654e..502400dbd8ae 100644 --- a/services/core/java/android/os/UserManagerInternal.java +++ b/services/core/java/android/os/UserManagerInternal.java @@ -279,4 +279,12 @@ public abstract boolean isSettingRestrictedForUser(String setting, int userId, S * Gets all {@link UserInfo UserInfos}. */ public abstract @NonNull UserInfo[] getUserInfos(); + + /** + * Returns {@code true} if the system should ignore errors when preparing + * the storage directories for the user with ID {@code userId}. This will + * return {@code false} for all new users; it will only return {@code true} + * for users that already existed on-disk from an older version of Android. + */ + public abstract boolean shouldIgnorePrepareStorageErrors(int userId); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8f0964663dab..780ecbf11f75 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3410,11 +3410,20 @@ private void prepareUserStorageInternal(String volumeUuid, int userId, int seria mInstaller.tryMountDataMirror(volumeUuid); } } - } catch (RemoteException | Installer.InstallerException e) { + } catch (Exception e) { Slog.wtf(TAG, e); // Make sure to re-throw this exception; we must not ignore failure // to prepare the user storage as it could indicate that encryption // wasn't successfully set up. + // + // Very unfortunately, these errors need to be ignored for broken + // users that already existed on-disk from older Android versions. + UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class); + if (umInternal.shouldIgnorePrepareStorageErrors(userId)) { + Slog.wtf(TAG, "ignoring error preparing storage for existing user " + userId + + "; device may be insecure!"); + return; + } throw new RuntimeException(e); } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index a768e8e02911..9576dc567c3f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -198,6 +198,8 @@ public class UserManagerService extends IUserManager.Stub { private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions"; private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL = "lastRequestQuietModeEnabledCall"; + private static final String TAG_IGNORE_PREPARE_STORAGE_ERRORS = + "ignorePrepareStorageErrors"; private static final String ATTR_KEY = "key"; private static final String ATTR_VALUE_TYPE = "type"; private static final String ATTR_MULTIPLE = "m"; @@ -298,6 +300,14 @@ static class UserData { private long mLastRequestQuietModeEnabledMillis; + /** + * {@code true} if the system should ignore errors when preparing the + * storage directories for this user. This is {@code false} for all new + * users; it will only be {@code true} for users that already existed + * on-disk from an older version of Android. + */ + private boolean mIgnorePrepareStorageErrors; + void setLastRequestQuietModeEnabledMillis(long millis) { mLastRequestQuietModeEnabledMillis = millis; } @@ -306,6 +316,14 @@ long getLastRequestQuietModeEnabledMillis() { return mLastRequestQuietModeEnabledMillis; } + boolean getIgnorePrepareStorageErrors() { + return mIgnorePrepareStorageErrors; + } + + void setIgnorePrepareStorageErrors() { + mIgnorePrepareStorageErrors = true; + } + void clearSeedAccountData() { seedAccountName = null; seedAccountType = null; @@ -2955,6 +2973,10 @@ void writeUserLP(UserData userData, OutputStream os) serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL); } + serializer.startTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS); + serializer.text(String.valueOf(userData.getIgnorePrepareStorageErrors())); + serializer.endTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS); + serializer.endTag(null, TAG_USER); serializer.endDocument(); @@ -3067,6 +3089,7 @@ UserData readUserLP(int id, InputStream is) throws IOException, Bundle legacyLocalRestrictions = null; RestrictionsSet localRestrictions = null; Bundle globalRestrictions = null; + boolean ignorePrepareStorageErrors = true; // default is true for old users XmlPullParser parser = Xml.newPullParser(); parser.setInput(is, StandardCharsets.UTF_8.name()); @@ -3158,6 +3181,11 @@ UserData readUserLP(int id, InputStream is) throws IOException, if (type == XmlPullParser.TEXT) { lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText()); } + } else if (TAG_IGNORE_PREPARE_STORAGE_ERRORS.equals(tag)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + ignorePrepareStorageErrors = Boolean.parseBoolean(parser.getText()); + } } } } @@ -3185,6 +3213,9 @@ UserData readUserLP(int id, InputStream is) throws IOException, userData.persistSeedData = persistSeedData; userData.seedAccountOptions = seedAccountOptions; userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp); + if (ignorePrepareStorageErrors) { + userData.setIgnorePrepareStorageErrors(); + } synchronized (mRestrictionsLock) { if (baseRestrictions != null) { @@ -4829,6 +4860,9 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(); } } + + pw.println(" Ignore errors preparing storage: " + + userData.getIgnorePrepareStorageErrors()); } } pw.println(); @@ -5254,6 +5288,14 @@ public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) return allInfos; } } + + @Override + public boolean shouldIgnorePrepareStorageErrors(int userId) { + synchronized (mUsersLock) { + UserData userData = mUsers.get(userId); + return userData != null && userData.getIgnorePrepareStorageErrors(); + } + } } /** From 05a1a227aa88272a2e112e30b2e9c651ed1c231a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 26 Mar 2022 01:08:07 +0000 Subject: [PATCH 051/208] [RESTRICT AUTOMERGE] Log to EventLog on prepareUserStorage failure Bug: 224585613 Change-Id: Id6dfb4f4c48d5cf4e71f54bdb6d0d6eea527caf5 (cherry picked from commit fbb632ea95ac5b6d9efa89e09d0988a9df4f19e4) Merged-In: Id6dfb4f4c48d5cf4e71f54bdb6d0d6eea527caf5 (cherry picked from commit 0762961674f1454b7c7012a0ab53c427570e836c) Merged-In: Id6dfb4f4c48d5cf4e71f54bdb6d0d6eea527caf5 --- .../core/java/com/android/server/StorageManagerService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 780ecbf11f75..f34b56df35e8 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -132,6 +132,7 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.DataUnit; +import android.util.EventLog; import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Pair; @@ -3411,6 +3412,7 @@ private void prepareUserStorageInternal(String volumeUuid, int userId, int seria } } } catch (Exception e) { + EventLog.writeEvent(0x534e4554, "224585613", -1, ""); Slog.wtf(TAG, e); // Make sure to re-throw this exception; we must not ignore failure // to prepare the user storage as it could indicate that encryption From 2ce42a764abee240686b6f19e2c2ef040c46a3a3 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 7 Jul 2021 16:19:44 -0400 Subject: [PATCH 052/208] Crash invalid FGS notifications Test: CTS, ActivityManagerProcessStateTest Fixes: 191981182 Change-Id: I13a0202b25c8118db47edba11a93c1939c94b392 (cherry picked from commit 6f657f8f5b7d41af426d6cd8d60bfda6e12057c0) (cherry picked from commit cb3c5c30092fb8527ff14118ccf04eae3a8363cb) Merged-In: I13a0202b25c8118db47edba11a93c1939c94b392 --- .../server/notification/NotificationManagerService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ddfe5b03d52a..8180c0404431 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5811,8 +5811,10 @@ void enqueueNotificationInternal(final String pkg, final String opPkg, final int // Fix the notification as best we can. try { fixNotification(notification, pkg, tag, id, userId); - } catch (Exception e) { + if (notification.isForegroundService()) { + throw new SecurityException("Invalid FGS notification", e); + } Slog.e(TAG, "Cannot fix notification", e); return; } From f80d4f6b63908738dad9e9bc778f60f5c3d54132 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Mon, 21 Feb 2022 17:42:22 +0800 Subject: [PATCH 053/208] [RESTRICT AUTOMERGE]Only allow system and same app to apply relinquishTaskIdentity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Any malicious application could hijack tasks by android:relinquishTaskIdentity. This vulnerability can perform UI spoofing or spy on user’s activities. This CL limit the usage which only allow system and same app to apply relinquishTaskIdentity. Based on the condition of updateIdentity from Task#setIntent, update the intent with the parent's task together to align with original code logic. Moreover, we shouldn't save launch params for such tasks with null realActivity. Bug: 185810717 Bug: 218243793 Test: atest IntentTests atest ActivityStarterTests atest TaskRecordTests atest testSplitscreenPortraitAppOrientationRequests Change-Id: I42c671e66c39c82be1dcef1f374d56d4593f9f57 (cherry picked from commit 7221a25a035bc7397492e15460b40395efce7023) Merged-In: I42c671e66c39c82be1dcef1f374d56d4593f9f57 --- .../server/wm/LaunchParamsPersister.java | 3 + .../core/java/com/android/server/wm/Task.java | 62 ++++++++++++++----- .../android/server/wm/TaskRecordTests.java | 44 +++++++++++++ 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index a974332fd852..b037e59942cf 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -214,6 +214,9 @@ void saveTask(Task task) { void saveTask(Task task, DisplayContent display) { final ComponentName name = task.realActivity; + if (name == null) { + return; + } final int userId = task.mUserId; PersistableLaunchParams params; ArrayMap map = mLaunchParamsMap.get(userId); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 2da9c8db9ff8..1887cb73848b 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -119,6 +119,7 @@ import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -227,6 +228,11 @@ class Task extends WindowContainer { // Do not move the stack as a part of reparenting static final int REPARENT_LEAVE_STACK_IN_PLACE = 2; + /** + * Used to identify if the activity that is installed from device's system image. + */ + boolean mIsEffectivelySystemApp; + String affinity; // The affinity name for this task, or null; may change identity. String rootAffinity; // Initial base affinity, or null; does not change from initial root. String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving @@ -477,11 +483,24 @@ private boolean processActivity(ActivityRecord r, if (r.finishing) return false; - // Set this as the candidate root since it isn't finishing. - mRoot = r; + if (mRoot == null || mRoot.finishing) { + // Set this as the candidate root since it isn't finishing. + mRoot = r; + } - // Only end search if we are ignore relinquishing identity or we are not relinquishing. - return ignoreRelinquishIdentity || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; + final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid; + if (ignoreRelinquishIdentity + || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0 + || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID + && !mRoot.info.applicationInfo.isSystemApp() + && mRoot.info.applicationInfo.uid != uid)) { + // No need to relinquish identity, end search. + return true; + } + + // Relinquish to next activity + mRoot = r; + return false; } } @@ -929,27 +948,35 @@ void setIntent(ActivityRecord r) { * @param info The activity info which could be different from {@code r.info} if set. */ void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) { - mCallingUid = r.launchedFromUid; - mCallingPackage = r.launchedFromPackage; - mCallingFeatureId = r.launchedFromFeatureId; - setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); - setLockTaskAuth(r); - - final WindowContainer parent = getParent(); - if (parent != null) { - final Task t = parent.asTask(); - if (t != null) { - t.setIntent(r); + boolean updateIdentity = false; + if (this.intent == null) { + updateIdentity = true; + } else if (!mNeverRelinquishIdentity) { + final ActivityInfo activityInfo = info != null ? info : r.info; + updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp + || effectiveUid == activityInfo.applicationInfo.uid); + } + if (updateIdentity) { + mCallingUid = r.launchedFromUid; + mCallingPackage = r.launchedFromPackage; + mCallingFeatureId = r.launchedFromFeatureId; + setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); + final WindowContainer parent = getParent(); + if (parent != null) { + final Task t = parent.asTask(); + if (t != null) { + t.setIntent(r); + } } } + setLockTaskAuth(r); } /** Sets the original intent, _without_ updating the calling uid or package. */ private void setIntent(Intent _intent, ActivityInfo info) { final boolean isLeaf = isLeafTask(); if (intent == null) { - mNeverRelinquishIdentity = - (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; + mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; } else if (mNeverRelinquishIdentity && isLeaf) { return; } @@ -962,6 +989,7 @@ private void setIntent(Intent _intent, ActivityInfo info) { rootAffinity = affinity; } effectiveUid = info.applicationInfo.uid; + mIsEffectivelySystemApp = info.applicationInfo.isSystemApp(); stringName = null; if (info.targetActivity == null) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index ddaa586fae8a..3be17b8fac19 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -616,6 +616,7 @@ public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { // one above as finishing. final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + task.effectiveUid = activity0.getUid(); final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); activity1.finishing = true; new ActivityBuilder(mService).setTask(task).build(); @@ -650,6 +651,7 @@ public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { // Set relinquishTaskIdentity for all activities in the task final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + task.effectiveUid = activity0.getUid(); final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; @@ -802,6 +804,7 @@ public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { // Make the current root activity relinquish task identity final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + task.effectiveUid = activity0.getUid(); // Add an extra activity on top - this will be the new root final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); // Add one more on top @@ -896,6 +899,47 @@ public void testUpdateEffectiveIntent_allFinishing() { verify(task).setIntent(eq(activity0)); } + /** + * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but + * another with different uid. This should make the task use the root activity when updating the + * intent. + */ + @Test + public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() { + final ActivityRecord activity0 = new ActivityBuilder(mService) + .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); + final Task task = activity0.getTask(); + + // Add an extra activity on top + new ActivityBuilder(mService).setUid(11).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + /** + * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity. + * This should make the task use the topmost activity when updating the intent. + */ + @Test + public void testUpdateEffectiveIntent_relinquishingMultipleActivities() { + final ActivityRecord activity0 = new ActivityBuilder(mService) + .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); + final Task task = activity0.getTask(); + task.effectiveUid = activity0.getUid(); + // Add an extra activity on top + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + // Add an extra activity on top + final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity2)); + } + @Test public void testSaveLaunchingStateWhenConfigurationChanged() { LaunchParamsPersister persister = mService.mStackSupervisor.mLaunchParamsPersister; From fd085b449206e0e750d509b971bed6f6b915df53 Mon Sep 17 00:00:00 2001 From: Wenhao Wang Date: Wed, 2 Feb 2022 10:56:44 -0800 Subject: [PATCH 054/208] DO NOT MERGE Suppress notifications when device enter lockdown This CL makes the following modifcations: 1. Add LockPatternUtils.StrongAuthTracker to monitor the lockdown mode status of the phone. 2. Call mListeners.notifyRemovedLocked with all the notifications in the mNotificationList when entering the lockdown mode. 3. Call mListeners.notifyPostedLocked with all the notifications in the mNotificationList when exiting the lockdown mode. 4. Dismiss the function calls of notifyPostedLocked, notifyRemovedLocked, and notifyRankingUpdateLocked during the lockdown mode. The CL also adds corresponding tests. Bug: 173721373 Test: atest NotificationManagerServiceTest Test: atest NotificationListenersTest Test: manually verify the paired device cannot receive notifications when the host phone is in lockdown mode. Ignore-AOSP-First: pending fix for a security issue. Change-Id: I7e83544863eeadf8272b6ff8a9bb8136d6466203 Merged-In: I7e83544863eeadf8272b6ff8a9bb8136d6466203 (cherry picked from commit 3cb6842a053e236cc98d7616ba4433c31ffda3ac) (cherry picked from commit 2e1c70b6b22422fb1f16be7add728327853f4618) Merged-In: I7e83544863eeadf8272b6ff8a9bb8136d6466203 --- .../NotificationManagerService.java | 107 +++++++++++++- .../tests/uiservicestests/AndroidManifest.xml | 1 + .../NotificationListenersTest.java | 133 ++++++++++++++++++ .../NotificationManagerServiceTest.java | 66 ++++++++- 4 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8180c0404431..5bd5c6067ea0 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -227,6 +227,7 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.StatsEvent; import android.util.TimeUtils; import android.util.Xml; @@ -258,6 +259,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; +import com.android.internal.widget.LockPatternUtils; import com.android.server.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.IoThread; @@ -1729,6 +1731,54 @@ static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { return out; } + protected class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + + SparseBooleanArray mUserInLockDownMode = new SparseBooleanArray(); + boolean mIsInLockDownMode = false; + + StrongAuthTracker(Context context) { + super(context); + } + + private boolean containsFlag(int haystack, int needle) { + return (haystack & needle) != 0; + } + + public boolean isInLockDownMode() { + return mIsInLockDownMode; + } + + @Override + public synchronized void onStrongAuthRequiredChanged(int userId) { + boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mUserInLockDownMode.put(userId, userInLockDownModeNext); + boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; + + if (mIsInLockDownMode == isInLockDownModeNext) { + return; + } + + if (isInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(); + } + + // When the mIsInLockDownMode is true, both notifyPostedLocked and + // notifyRemovedLocked will be dismissed. So we shall call + // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode + // as true and call postNotificationsWhenExitLockDownMode after we set + // mIsInLockDownMode as false. + mIsInLockDownMode = isInLockDownModeNext; + + if (!isInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(); + } + } + } + + private LockPatternUtils mLockPatternUtils; + private StrongAuthTracker mStrongAuthTracker; + public NotificationManagerService(Context context) { this(context, new NotificationRecordLoggerImpl(), @@ -1754,6 +1804,11 @@ void setAudioManager(AudioManager audioMananger) { mAudioManager = audioMananger; } + @VisibleForTesting + void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) { + mStrongAuthTracker = strongAuthTracker; + } + @VisibleForTesting void setKeyguardManager(KeyguardManager keyguardManager) { mKeyguardManager = keyguardManager; @@ -1941,6 +1996,8 @@ void init(WorkerHandler handler, RankingHandler rankingHandler, ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); + mLockPatternUtils = new LockPatternUtils(getContext()); + mStrongAuthTracker = new StrongAuthTracker(getContext()); String[] extractorNames; try { extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); @@ -2137,7 +2194,8 @@ public void onStart() { init(handler, new RankingHandlerWorker(mRankingThread.getLooper()), AppGlobals.getPackageManager(), getContext().getPackageManager(), getLocalService(LightsManager.class), - new NotificationListeners(AppGlobals.getPackageManager()), + new NotificationListeners(getContext(), mNotificationLock, mUserProfiles, + AppGlobals.getPackageManager()), new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles, AppGlobals.getPackageManager()), new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()), @@ -2387,6 +2445,7 @@ public void onBootPhase(int phase) { bubbsExtractor.setShortcutHelper(mShortcutHelper); } registerNotificationPreferencesPullers(); + mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to // bind to listener services. @@ -8627,6 +8686,29 @@ protected void unhideNotificationsForPackages(String[] pkgs) { } } + private void cancelNotificationsWhenEnterLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, + rec.getStats()); + } + + } + } + + private void postNotificationsWhenExitLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyPostedLocked(rec, rec); + } + + } + } + private void updateNotificationPulse() { synchronized (mNotificationLock) { updateLightsLocked(); @@ -8868,6 +8950,10 @@ private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo inf rankings.toArray(new NotificationListenerService.Ranking[0])); } + boolean isInLockDownMode() { + return mStrongAuthTracker.isInLockDownMode(); + } + boolean hasCompanionDevice(ManagedServiceInfo info) { if (mCompanionManager == null) { mCompanionManager = getCompanionManager(); @@ -9448,8 +9534,9 @@ public class NotificationListeners extends ManagedServices { private final ArraySet mLightTrimListeners = new ArraySet<>(); - public NotificationListeners(IPackageManager pm) { - super(getContext(), mNotificationLock, mUserProfiles, pm); + public NotificationListeners(Context context, Object lock, UserProfiles userProfiles, + IPackageManager pm) { + super(context, lock, userProfiles, pm); } @Override @@ -9583,8 +9670,12 @@ public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) { * targetting <= O_MR1 */ @GuardedBy("mNotificationLock") - private void notifyPostedLocked(NotificationRecord r, NotificationRecord old, + void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { + if (isInLockDownMode()) { + return; + } + try { // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.getSbn(); @@ -9681,6 +9772,10 @@ private void updateUriPermissionsForActiveNotificationsLocked( @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { + if (isInLockDownMode()) { + return; + } + final StatusBarNotification sbn = r.getSbn(); // make a copy in case changes are made to the underlying Notification object @@ -9726,6 +9821,10 @@ public void notifyRemovedLocked(NotificationRecord r, int reason, */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List changedHiddenNotifications) { + if (isInLockDownMode()) { + return; + } + boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 767857bf2de8..e8e3a8f84f21 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -33,6 +33,7 @@ + diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java new file mode 100644 index 000000000000..7244fcdda731 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.notification; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.INotificationManager; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.service.notification.NotificationStats; +import android.service.notification.StatusBarNotification; +import android.testing.TestableContext; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.FieldSetter; + +import java.util.List; + +public class NotificationListenersTest extends UiServiceTestCase { + + @Mock + private PackageManager mPm; + @Mock + private IPackageManager miPm; + + @Mock + NotificationManagerService mNm; + @Mock + private INotificationManager mINm; + private TestableContext mContext = spy(getContext()); + + NotificationManagerService.NotificationListeners mListeners; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + getContext().setMockPackageManager(mPm); + doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + + mListeners = spy(mNm.new NotificationListeners( + mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm)); + when(mNm.getBinderService()).thenReturn(mINm); + } + + + @Test + public void testNotifyPostedLockedInLockdownMode() { + NotificationRecord r = mock(NotificationRecord.class); + NotificationRecord old = mock(NotificationRecord.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(old); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, never()).getSbn(); + } + + @Test + public void testnotifyRankingUpdateLockedInLockdownMode() { + List chn = mock(List.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, atLeast(1)).size(); + + // in the lockdown mode + reset(chn); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, never()).size(); + } + + @Test + public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { + NotificationRecord r = mock(NotificationRecord.class); + NotificationStats rs = mock(NotificationStats.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); + FieldSetter.setField(mNm, + NotificationManagerService.class.getDeclaredField("mHandler"), + mock(NotificationManagerService.WorkerHandler.class)); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(rs); + when(mNm.isInLockDownMode()).thenReturn(true); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, never()).getSbn(); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 6e8b1d91196c..f1d91e9daf8c 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -52,9 +52,12 @@ import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; +import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -398,8 +401,26 @@ private void setNotificationAssistantAccessGrantedCallback( interface NotificationAssistantAccessGrantedCallback { void onGranted(ComponentName assistant, int userId, boolean granted); } + + class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { + private int mGetStrongAuthForUserReturnValue = 0; + StrongAuthTrackerFake(Context context) { + super(context); + } + + public void setGetStrongAuthForUserReturnValue(int val) { + mGetStrongAuthForUserReturnValue = val; + } + + @Override + public int getStrongAuthForUser(int userId) { + return mGetStrongAuthForUserReturnValue; + } + } } + TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; + private class TestableToastCallback extends ITransientNotification.Stub { @Override public void show(IBinder windowToken) { @@ -516,6 +537,9 @@ null, new ComponentName(PKG, "test_class"), mService.setAudioManager(mAudioManager); + mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext); + mService.setStrongAuthTracker(mStrongAuthTracker); + mShortcutHelper = mService.getShortcutHelper(); mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); @@ -7129,4 +7153,44 @@ public void testGetActiveNotification_filtersUsers() throws Exception { } } } -} + + @Test + public void testStrongAuthTracker_isInLockDownMode() { + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertFalse(mStrongAuthTracker.isInLockDownMode()); + } + + @Test + public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { + // post 2 notifications from 2 packages + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("b", 1001, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgB); + + // when entering the lockdown mode, cancel the 2 notifications. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + + // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN. + ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); + verify(mListeners, times(2)).notifyRemovedLocked(any(), captor.capture(), any()); + assertEquals(REASON_CANCEL_ALL, captor.getValue().intValue()); + + // exit lockdown mode. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + + // the notifyPostedLocked function is called twice. + verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + } +} \ No newline at end of file From 98f8c51ad2bb81017ca0a673d90067a45158c449 Mon Sep 17 00:00:00 2001 From: Hui Yu Date: Sat, 7 May 2022 21:43:23 -0700 Subject: [PATCH 055/208] Make sure callingPackage belongs to callingUid when checking BG-FGS restrictions. This is to stop spoofed packageName to pretend to be allowListed packageName so it can bypass the BG-FGS restriction. This applies to both BG-FGS while-in-use restriction and BG-FGS-start restriction since these two restrictions are related. Bug: 216695100 Bug: 215003903 Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testSpoofPackageName Change-Id: Ic14fc331a9b5fbdbcfe6e54a31c8b765513bfd89 Merged-In: Ic14fc331a9b5fbdbcfe6e54a31c8b765513bfd89 BYPASS_INCLUSIVE_LANGUAGE_REASON=Legacy API (cherry picked from commit 023509e4871c0dafb842dc812bfa62e8d59cbfae) Merged-In: Ic14fc331a9b5fbdbcfe6e54a31c8b765513bfd89 --- .../com/android/server/am/ActiveServices.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index aa38fd1e6fc4..25c4844a2984 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -5001,14 +5001,16 @@ private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage return true; } - if (mAm.mInternal.isTempAllowlistedForFgsWhileInUse(callingUid)) { - return true; - } - - final boolean isWhiteListedPackage = - mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage); - if (isWhiteListedPackage) { - return true; + if (verifyPackage(callingPackage, callingUid)) { + final boolean isWhiteListedPackage = + mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage); + if (isWhiteListedPackage) { + return true; + } + } else { + EventLog.writeEvent(0x534e4554, "215003903", callingUid, + "callingPackage:" + callingPackage + " does not belong to callingUid:" + + callingUid); } // Is the calling UID a device owner app? @@ -5047,4 +5049,21 @@ private void resetFgsRestrictionLocked(ServiceRecord r) { r.mAllowWhileInUsePermissionInFgs = false; r.mLastSetFgsRestrictionTime = 0; } + + /** + * Checks if a given packageName belongs to a given uid. + * @param packageName the package of the caller + * @param uid the uid of the caller + * @return true or false + */ + private boolean verifyPackage(String packageName, int uid) { + if (uid == ROOT_UID || uid == SYSTEM_UID) { + //System and Root are always allowed + return true; + } + final int userId = UserHandle.getUserId(uid); + final int packageUid = mAm.getPackageManagerInternalLocked() + .getPackageUid(packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); + return UserHandle.isSameApp(uid, packageUid); + } } From f05af87c97bb36016e83f0df3db21d8d358ec9e9 Mon Sep 17 00:00:00 2001 From: lucaslin Date: Wed, 30 Mar 2022 20:42:43 +0800 Subject: [PATCH 056/208] Clear mInterface before calling resetIkeState() Clear mInterface before calling resetIkeState() in onDefaultNetworkChanged(). resetIkeState() will trigger interfaceRemoved() to be called. If mInterface is set, interfaceRemoved() will clear Ikev2VpnRunner which makes VPN disconnect. This issue can be reproduced when device establishes VPN connection with mobile data first then connects to wifi. In this case, onLost() for mobile data will not be called because there is a new network(wifi) can satisfy the request, so only onAvailable() for wifi will be called. Which means onSessionLost() will not be called and only onDefaultNetworkChanged() will be called, which makes that mInterface is not cleared before interfaceRemoved() is called. Bug: 219546241 Test: Check if VPN is still there when establishing VPN with mobile data first, then connect to wifi and disconnect wifi. Change-Id: I7f9a1d9afd2a40762e9fac68edf1fb8ae75df8bc (cherry picked from commit 520cc2fde363dd038911b98b8b46259faf58a659) Merged-In: I7f9a1d9afd2a40762e9fac68edf1fb8ae75df8bc (cherry picked from commit 65d44b93bb99eae441ebf5bf1afb4efd00074758) Merged-In: I7f9a1d9afd2a40762e9fac68edf1fb8ae75df8bc --- services/core/java/com/android/server/connectivity/Vpn.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 8f9b5ca26e1b..fd5b46cd0ff7 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2523,6 +2523,9 @@ public void onDefaultNetworkChanged(@NonNull Network network) { return; // VPN has been shut down. } + // Clear mInterface to prevent Ikev2VpnRunner being cleared when + // interfaceRemoved() is called. + mInterface = null; // Without MOBIKE, we have no way to seamlessly migrate. Close on old // (non-default) network, and start the new one. resetIkeState(); From 11c5d57eff899f2194f26dac92b541f86372f3ed Mon Sep 17 00:00:00 2001 From: Raphael Kim Date: Fri, 22 Apr 2022 00:32:08 +0000 Subject: [PATCH 057/208] Remove package title from notification access confirmation intent Bug: 228178437 Test: Manually confirmed on an application Change-Id: Idad6dc0c71d7b39de0bd9e4ad922b5e6020a6184 Merged-In: Idad6dc0c71d7b39de0bd9e4ad922b5e6020a6184 (cherry picked from commit 51d47ec7c875cf964f46965a27a5d36343ea999d) Merged-In: Idad6dc0c71d7b39de0bd9e4ad922b5e6020a6184 --- ...ificationAccessConfirmationActivityContract.java | 10 ++++++---- .../companion/CompanionDeviceManagerService.java | 13 ++----------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java index 4ce6f609ef73..fdf0e9046eef 100644 --- a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java +++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java @@ -17,6 +17,7 @@ package com.android.internal.notification; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; public final class NotificationAccessConfirmationActivityContract { @@ -25,13 +26,14 @@ public final class NotificationAccessConfirmationActivityContract { "com.android.settings.notification.NotificationAccessConfirmationActivity"); public static final String EXTRA_USER_ID = "user_id"; public static final String EXTRA_COMPONENT_NAME = "component_name"; - public static final String EXTRA_PACKAGE_TITLE = "package_title"; - public static Intent launcherIntent(int userId, ComponentName component, String packageTitle) { + /** + * Creates a launcher intent for NotificationAccessConfirmationActivity. + */ + public static Intent launcherIntent(Context context, int userId, ComponentName component) { return new Intent() .setComponent(COMPONENT_NAME) .putExtra(EXTRA_USER_ID, userId) - .putExtra(EXTRA_COMPONENT_NAME, component) - .putExtra(EXTRA_PACKAGE_TITLE, packageTitle); + .putExtra(EXTRA_COMPONENT_NAME, component); } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index ad91924c1e63..9f503419a7cf 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -43,7 +43,6 @@ import android.content.SharedPreferences; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; -import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; import android.os.Binder; @@ -385,20 +384,12 @@ public PendingIntent requestNotificationAccess(ComponentName component) String callingPackage = component.getPackageName(); checkCanCallNotificationApi(callingPackage); int userId = getCallingUserId(); - String packageTitle = BidiFormatter.getInstance().unicodeWrap( - getPackageInfo(callingPackage, userId) - .applicationInfo - .loadSafeLabel(getContext().getPackageManager(), - PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, - PackageItemInfo.SAFE_LABEL_FLAG_TRIM - | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE) - .toString()); - long identity = Binder.clearCallingIdentity(); + final long identity = Binder.clearCallingIdentity(); try { return PendingIntent.getActivityAsUser(getContext(), 0 /* request code */, NotificationAccessConfirmationActivityContract.launcherIntent( - userId, component, packageTitle), + getContext(), userId, component), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null /* options */, From 64cfbc7f08af1f359b61ed57061f5ab561cc82c0 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Fri, 27 May 2022 11:58:11 +0800 Subject: [PATCH 058/208] [RESTRICT AUTOMERGE]Prevent set intent on non-leaf tasks The root task was created with null intent, but the intent, resize mode and other information were updated from child tasks, which sets the split-screen-secondary root task to unresizable. This CL is back porting from f666e494af to prevent setting the split-screen-secondary root task to unresizable. Bug: 230435065 Bug: 185810717 Test: atest IntentTests atest ActivityStarterTests atest TaskRecordTests atest testSplitscreenPortraitAppOrientationRequests Change-Id: I856d66371ac121f681958a7129527f18a5357d0f (cherry picked from commit c5aaf99df5656fde68a4aaf2801fe30b0f5e44ae) Merged-In: I856d66371ac121f681958a7129527f18a5357d0f --- services/core/java/com/android/server/wm/Task.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1887cb73848b..0d1a3436d4cc 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -961,27 +961,20 @@ void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo mCallingPackage = r.launchedFromPackage; mCallingFeatureId = r.launchedFromFeatureId; setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); - final WindowContainer parent = getParent(); - if (parent != null) { - final Task t = parent.asTask(); - if (t != null) { - t.setIntent(r); - } - } } setLockTaskAuth(r); } /** Sets the original intent, _without_ updating the calling uid or package. */ private void setIntent(Intent _intent, ActivityInfo info) { - final boolean isLeaf = isLeafTask(); + if (!isLeafTask()) return; if (intent == null) { mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; - } else if (mNeverRelinquishIdentity && isLeaf) { + } else if (mNeverRelinquishIdentity) { return; } - affinity = isLeaf ? info.taskAffinity : null; + affinity = info.taskAffinity; if (intent == null) { // If this task already has an intent associated with it, don't set the root // affinity -- we don't want it changing after initially set, but the initially From 53607b12e0deef0b769abf121c981b0905a65174 Mon Sep 17 00:00:00 2001 From: chiachangwang Date: Thu, 2 Jun 2022 10:22:20 +0000 Subject: [PATCH 059/208] Stop using invalid URL to prevent unexpected crash Verify the input PAC Uri before performing follow-up actions. Check if the URL is a valid URL to filter some invalid URLs since these invalid URLs could not fall into any subclass of existing URLConnections. When the PAC Uri is other invalid URL scheme, it will cause an UnsupportedOperationException if there is no proper subclass that implements the openConnection() method. A malformed URL may crash the system. Even it's a valid URL, some subclasses(e.g. JarURLConnection) may not have openConnection() implemented. It will also hit the problem, so convert the possbile exception from openConnection() to re-throw it to IOException which is handled in the existing code. Bug: 219498290 Test: atest FrameworksNetTests CtsNetTestCases Test: Test with malformed URL Merged-In: I22903414380b62051f514e43b93af992f45740b4 Merged-In: I2abff75ec59a17628ef006aad348c53fadbed076 Change-Id: I4d6cec1da9cf3f70dec0dcf4223254d3da4f30a3 (cherry picked from commit 6390b37a3b32fc7583154d53fda3af8fbd95f59f) (cherry picked from commit 6d6f4106948bbad67b9845603392d084078997c4) Merged-In: I4d6cec1da9cf3f70dec0dcf4223254d3da4f30a3 --- .../server/connectivity/PacManager.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index f6ce2dc68b99..621c9c71df58 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -37,6 +37,7 @@ import android.os.SystemProperties; import android.provider.Settings; import android.util.Log; +import android.webkit.URLUtil; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.TrafficStatsConstants; @@ -215,8 +216,22 @@ synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { * @throws IOException if the URL is malformed, or the PAC file is too big. */ private static String get(Uri pacUri) throws IOException { - URL url = new URL(pacUri.toString()); - URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY); + if (!URLUtil.isValidUrl(pacUri.toString())) { + throw new IOException("Malformed URL:" + pacUri); + } + + final URL url = new URL(pacUri.toString()); + URLConnection urlConnection; + try { + urlConnection = url.openConnection(java.net.Proxy.NO_PROXY); + // Catch the possible exceptions and rethrow as IOException to not to crash the system + // for illegal input. + } catch (IllegalArgumentException e) { + throw new IOException("Incorrect proxy type for " + pacUri); + } catch (UnsupportedOperationException e) { + throw new IOException("Unsupported URL connection type for " + pacUri); + } + long contentLength = -1; try { contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length")); From b17f4b0eee4ce0596581aca37c607dac9b3e06b5 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Tue, 19 Apr 2022 10:54:18 -0700 Subject: [PATCH 060/208] Only allow the system server to connect to sync adapters Bug: 203229608 Test: Manual test with changing the check logic + debug log Change-Id: If18009f61360564d02dcda9b1e5fa15685e3250f (cherry picked from commit 58270527d11ac7e5f07d337a402d8edf046a63ee) (cherry picked from commit 7d1397a54475ed7fee632339ef7c60b432f0fbff) Merged-In: If18009f61360564d02dcda9b1e5fa15685e3250f --- .../content/AbstractThreadedSyncAdapter.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java index a086a308d0d9..da4ecdd8c1f2 100644 --- a/core/java/android/content/AbstractThreadedSyncAdapter.java +++ b/core/java/android/content/AbstractThreadedSyncAdapter.java @@ -21,6 +21,7 @@ import android.accounts.Account; import android.annotation.MainThread; import android.annotation.NonNull; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -171,8 +172,20 @@ private Account toSyncKey(Account account) { } private class ISyncAdapterImpl extends ISyncAdapter.Stub { + private boolean isCallerSystem() { + final long callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + android.util.EventLog.writeEvent(0x534e4554, "203229608", -1, ""); + return false; + } + return true; + } + @Override public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) { + if (!isCallerSystem()) { + return; + } Handler.getMain().sendMessage(obtainMessage( AbstractThreadedSyncAdapter::handleOnUnsyncableAccount, AbstractThreadedSyncAdapter.this, cb)); @@ -181,12 +194,16 @@ public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) { @Override public void startSync(ISyncContext syncContext, String authority, Account account, Bundle extras) { + if (!isCallerSystem()) { + return; + } if (ENABLE_LOG) { if (extras != null) { extras.size(); // Unparcel so its toString() will show the contents. } Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras); } + try { final SyncContext syncContextClient = new SyncContext(syncContext); @@ -242,6 +259,9 @@ public void startSync(ISyncContext syncContext, String authority, Account accoun @Override public void cancelSync(ISyncContext syncContext) { + if (!isCallerSystem()) { + return; + } try { // synchronize to make sure that mSyncThreads doesn't change between when we // check it and when we use it From bd1cab7e4bf38d76967eb8adc89c0fa4689292ad Mon Sep 17 00:00:00 2001 From: Manjeet Rulhania Date: Thu, 28 Apr 2022 21:50:25 +0000 Subject: [PATCH 061/208] Fix duplicate permission privilege escalation Duplicate permissions definition with different group allows privilege permission escalation to a different permission group. Android studio and gradle plugin does not allow duplicate permissions with different attributes, these tools only allow if duplicate permissions are exact copies. Also platform stores permissions in map at multiple places with permission name as key. This suggests that we can disallow duplicate permissions during package install/update Bug: 213323615 Test: AppSecurityTests Change-Id: I1910dca44104e35a57eba4acfa8188cd9b8626ac Merged-Id: I34120fff2ec2a158dfa55779d2afd4bbd49487ff Merged-In: I9bc839836786a0876e67fd73c05f8944bb532249 (cherry picked from commit f9a9dc720c151ce9d8a2bba6f5400f5a8f759b2d) Merged-In: I1910dca44104e35a57eba4acfa8188cd9b8626ac --- .../pm/parsing/ParsingPackageUtils.java | 8 +++ .../component/ParsedPermissionUtils.java | 49 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index a56a3ea9b2f6..e4fd59c01eac 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; @@ -778,6 +779,13 @@ private ParseResult parseBaseApkTags(ParseInput input, ParsingPa ); } + if (ParsedPermissionUtils.declareDuplicatePermission(pkg)) { + return input.error( + INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Found duplicate permission with a different attribute value." + ); + } + convertNewPermissions(pkg); convertSplitPermissions(pkg); diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java index 1884a1e27832..9a6742c68ab4 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java @@ -22,6 +22,8 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.util.ArrayMap; +import android.util.EventLog; import android.util.Slog; import com.android.internal.R; @@ -32,6 +34,8 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.List; +import java.util.Objects; /** @hide */ public class ParsedPermissionUtils { @@ -207,4 +211,49 @@ public static ParseResult parsePermissionGroup(ParsingPac return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permissionGroup, input); } + + /** + * Determines if a duplicate permission is malformed .i.e. defines different protection level + * or group. + */ + private static boolean isMalformedDuplicate(ParsedPermission p1, ParsedPermission p2) { + // Since a permission tree is also added as a permission with normal protection + // level, we need to skip if the parsedPermission is a permission tree. + if (p1 == null || p2 == null || p1.isTree() || p2.isTree()) { + return false; + } + + if (p1.getProtectionLevel() != p2.getProtectionLevel()) { + return true; + } + if (!Objects.equals(p1.getGroup(), p2.getGroup())) { + return true; + } + + return false; + } + + /** + * @return {@code true} if the package declares malformed duplicate permissions. + */ + public static boolean declareDuplicatePermission(@NonNull ParsingPackage pkg) { + final List permissions = pkg.getPermissions(); + final int size = permissions.size(); + if (size > 0) { + final ArrayMap checkDuplicatePerm = new ArrayMap<>(size); + for (int i = 0; i < size; i++) { + final ParsedPermission parsedPermission = permissions.get(i); + final String name = parsedPermission.getName(); + final ParsedPermission perm = checkDuplicatePerm.get(name); + if (isMalformedDuplicate(parsedPermission, perm)) { + // Fix for b/213323615 + EventLog.writeEvent(0x534e4554, "213323615", + "The package " + pkg.getPackageName() + " seems malicious"); + return true; + } + checkDuplicatePerm.put(name, parsedPermission); + } + } + return false; + } } From afddff33b9f2b0d8d5db7512d84ef21668e1a6c5 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 30 Mar 2022 21:46:29 +0000 Subject: [PATCH 062/208] Parcel: recycle recycles Before, it was like getting a used pan with food stuck on it. We run a clean ship here. You want a Parcel? You get a fresh Parcel. When we recycle a Parcel, we do a real clean-up job. Air freshener. All bits brushed over. These Parcel objects are clean as heck now! (specifically cleans mClassCookies) Bug: 208279300 Test: build Merged-In: I250872f5c6796bb64e2dc68008154c0e90feb218 Change-Id: I250872f5c6796bb64e2dc68008154c0e90feb218 (cherry picked from commit 46770fa49c9a5e51a5ea5a3afc7aab0dba2e59bd) (cherry picked from commit b5c79e141a81fa86fc834980d46886ac3c86ab11) Merged-In: I250872f5c6796bb64e2dc68008154c0e90feb218 --- core/java/android/os/Parcel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index e5bab6fc9230..5a4b22b8a91b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -443,6 +443,7 @@ public static Parcel obtain() { */ public final void recycle() { if (DEBUG_RECYCLE) mStack = null; + mClassCookies = null; freeBuffer(); final Parcel[] pool; From 269552610f03b338383ebb4426bbb36f8f41e222 Mon Sep 17 00:00:00 2001 From: Manjeet Rulhania Date: Thu, 30 Jun 2022 22:09:09 +0000 Subject: [PATCH 063/208] Remove package name from SafetyNet logs Bug: 213323615 Test: AppSecurityTests Change-Id: I993832e148636f1795ffe393c6dc74a08b9442f8 Merged-In: I8f823487ca16861a35135cfc3383fa2ce8258017 Merged-In: I4b61d13256ce0bfb8fc9d21db52ee78ce2097f14 (cherry picked from commit bbe2a118277d9b225a272c53e509fdeb2fe0a993) Merged-In: I993832e148636f1795ffe393c6dc74a08b9442f8 --- .../content/pm/parsing/component/ParsedPermissionUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java index 9a6742c68ab4..fa7cfceb1a4c 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java @@ -247,8 +247,7 @@ public static boolean declareDuplicatePermission(@NonNull ParsingPackage pkg) { final ParsedPermission perm = checkDuplicatePerm.get(name); if (isMalformedDuplicate(parsedPermission, perm)) { // Fix for b/213323615 - EventLog.writeEvent(0x534e4554, "213323615", - "The package " + pkg.getPackageName() + " seems malicious"); + EventLog.writeEvent(0x534e4554, "213323615"); return true; } checkDuplicatePerm.put(name, parsedPermission); From 9e399bb1963912b7ecf91d657fb88a4bd951b6fe Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Wed, 16 Feb 2022 18:57:24 +0100 Subject: [PATCH 064/208] Restrict getInputMethodWindowVisibleHeight Make sure only the app currently interacting with the IME can query this, and restrict the API to apps targeting SDKs before T Fixes: 204906124 Test: atest 'InputMethodManagerTest#getInputMethodWindowVisibleHeight_returnsZeroIfNotFocused' Change-Id: If1da19a3dd8c29542afc970b4b201d87547c27a9 Merged-In: If1da19a3dd8c29542afc970b4b201d87547c27a9 (cherry picked from commit fd7847b53344edb46d0b62fbdfb3f5b12ba6ac9e) Merged-In: If1da19a3dd8c29542afc970b4b201d87547c27a9 --- .../view/inputmethod/InputMethodManager.java | 2 +- .../internal/view/IInputMethodManager.aidl | 2 +- .../InputMethodManagerService.java | 69 ++++++++++++++----- .../MultiClientInputMethodManagerService.java | 2 +- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 5f8b13b5b548..7b2ebde70e5f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2957,7 +2957,7 @@ public Map> getShortcutInputMethodsAnd @UnsupportedAppUsage public int getInputMethodWindowVisibleHeight() { try { - return mService.getInputMethodWindowVisibleHeight(); + return mService.getInputMethodWindowVisibleHeight(mClient); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index a1cbd3fcae79..629d92927237 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -67,7 +67,7 @@ interface IInputMethodManager { void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); // This is kept due to @UnsupportedAppUsage. // TODO(Bug 113914148): Consider removing this. - int getInputMethodWindowVisibleHeight(); + int getInputMethodWindowVisibleHeight(in IInputMethodClient client); void reportActivityView(in IInputMethodClient parentClient, int childDisplayId, in float[] matrixValues); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3e7437d1ee7e..eca500ec612b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -102,6 +102,7 @@ import android.util.Printer; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.ContextThemeWrapper; import android.view.DisplayInfo; import android.view.IWindowManager; @@ -317,6 +318,8 @@ private static final class DebugFlags { final InputMethodSettings mSettings; final SettingsObserver mSettingsObserver; final IWindowManager mIWindowManager; + private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid = + new SparseBooleanArray(0); final WindowManagerInternal mWindowManagerInternal; final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; @@ -1409,6 +1412,13 @@ public void onFinishPackageChanges() { clearPackageChangeState(); } + @Override + public void onUidRemoved(int uid) { + synchronized (mMethodMap) { + mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.delete(uid); + } + } + private void clearPackageChangeState() { // No need to lock them because we access these fields only on getRegisteredHandler(). mChangedPackages.clear(); @@ -3185,20 +3195,8 @@ public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int } final long ident = Binder.clearCallingIdentity(); try { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - final ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " + client.asBinder()); - } - if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, - cs.selfReportedDisplayId)) { - Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); - return false; - } + if (!canInteractWithImeLocked(uid, client, "showSoftInput")) { + return false; } if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); return showCurrentInputLocked(windowToken, flags, resultReceiver, @@ -3979,9 +3977,46 @@ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()} */ @Override - public int getInputMethodWindowVisibleHeight() { - // TODO(yukawa): Should we verify the display ID? - return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); + @Deprecated + public int getInputMethodWindowVisibleHeight(@NonNull IInputMethodClient client) { + int callingUid = Binder.getCallingUid(); + return Binder.withCleanCallingIdentity(() -> { + final int curTokenDisplayId; + synchronized (mMethodMap) { + if (!canInteractWithImeLocked(callingUid, client, + "getInputMethodWindowVisibleHeight")) { + if (!mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.get(callingUid)) { + EventLog.writeEvent(0x534e4554, "204906124", callingUid, ""); + mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.put(callingUid, true); + } + return 0; + } + // This should probably use the caller's display id, but because this is unsupported + // and maintained only for compatibility, there's no point in fixing it. + curTokenDisplayId = mCurTokenDisplayId; + } + return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId); + }); + } + + private boolean canInteractWithImeLocked(int callingUid, IInputMethodClient client, + String method) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + final ClientState cs = mClients.get(client.asBinder()); + if (cs == null) { + throw new IllegalArgumentException("unknown client " + client.asBinder()); + } + if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, + cs.selfReportedDisplayId)) { + Slog.w(TAG, "Ignoring " + method + " of uid " + callingUid + ": " + client); + return false; + } + } + return true; } @Override diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 19dff9807075..3429e7bb6bba 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1754,7 +1754,7 @@ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] @BinderThread @Override - public int getInputMethodWindowVisibleHeight() { + public int getInputMethodWindowVisibleHeight(IInputMethodClient client) { reportNotSupported(); return 0; } From 31e8422115138d4fa8da63be16ca97ebe723e436 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 1 Jul 2022 09:49:12 -0400 Subject: [PATCH 065/208] Limit the number of concurrently snoozed notifications Test: atest FrameworksUiServicesTests Bug: 234441463 Change-Id: I005b43979d1c708fd505c8b33ae0c8cb03ddbb35 Merged-In: I005b43979d1c708fd505c8b33ae0c8cb03ddbb35 (cherry picked from commit 7c38394ae9c69620499a87e629edae4fe0ac4edc) (cherry picked from commit 455a525421ea3748172d84a2529b9d993491a28f) Merged-In: I005b43979d1c708fd505c8b33ae0c8cb03ddbb35 --- .../NotificationManagerService.java | 19 +++-- .../server/notification/SnoozeHelper.java | 11 +++ .../NotificationManagerServiceTest.java | 83 +++++++++++++++++-- .../server/notification/SnoozeHelperTest.java | 17 ++++ 4 files changed, 116 insertions(+), 14 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 5bd5c6067ea0..dd8f3e11b575 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6335,6 +6335,7 @@ public void run() { @GuardedBy("mNotificationLock") void snoozeLocked(NotificationRecord r) { + final List recordsToSnooze = new ArrayList<>(); if (r.getSbn().isGroup()) { final List groupNotifications = findCurrentAndSnoozedGroupNotificationsLocked( @@ -6343,8 +6344,8 @@ void snoozeLocked(NotificationRecord r) { if (r.getNotification().isGroupSummary()) { // snooze all children for (int i = 0; i < groupNotifications.size(); i++) { - if (mKey != groupNotifications.get(i).getKey()) { - snoozeNotificationLocked(groupNotifications.get(i)); + if (!mKey.equals(groupNotifications.get(i).getKey())) { + recordsToSnooze.add(groupNotifications.get(i)); } } } else { @@ -6354,8 +6355,8 @@ void snoozeLocked(NotificationRecord r) { if (groupNotifications.size() == 2) { // snooze summary and the one child for (int i = 0; i < groupNotifications.size(); i++) { - if (mKey != groupNotifications.get(i).getKey()) { - snoozeNotificationLocked(groupNotifications.get(i)); + if (!mKey.equals(groupNotifications.get(i).getKey())) { + recordsToSnooze.add(groupNotifications.get(i)); } } } @@ -6363,7 +6364,15 @@ void snoozeLocked(NotificationRecord r) { } } // snooze the notification - snoozeNotificationLocked(r); + recordsToSnooze.add(r); + + if (mSnoozeHelper.canSnooze(recordsToSnooze.size())) { + for (int i = 0; i < recordsToSnooze.size(); i++) { + snoozeNotificationLocked(recordsToSnooze.get(i)); + } + } else { + Log.w(TAG, "Cannot snooze " + r.getKey() + ": too many snoozed notifications"); + } } diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index da472be81156..a29ac131f94c 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -60,6 +60,8 @@ public class SnoozeHelper { public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1"; + static final int CONCURRENT_SNOOZE_LIMIT = 500; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -132,6 +134,15 @@ void cleanupPersistedContext(String key){ } } + protected boolean canSnooze(int numberToSnooze) { + synchronized (mLock) { + if ((mPackages.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) { + return false; + } + } + return true; + } + @NonNull protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) { Long time = null; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index f1d91e9daf8c..88d7680d9236 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -2795,20 +2795,81 @@ public void testHasCompanionDevice_noService() { assertFalse(mService.hasCompanionDevice(mListener)); } + @Test + public void testSnoozeRunnable_tooManySnoozed_singleNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + mService.addNotification(notification); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(1)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notification.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertEquals(1, mService.getNotificationRecordCount()); + } + + @Test + public void testSnoozeRunnable_tooManySnoozed_singleGroupChildNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + final NotificationRecord notificationChild = generateNotificationRecord( + mTestNotificationChannel, 1, "group", false); + mService.addNotification(notification); + mService.addNotification(notificationChild); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(2)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notificationChild.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertEquals(2, mService.getNotificationRecordCount()); + } + + @Test + public void testSnoozeRunnable_tooManySnoozed_summaryNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + final NotificationRecord notificationChild = generateNotificationRecord( + mTestNotificationChannel, 12, "group", false); + final NotificationRecord notificationChild2 = generateNotificationRecord( + mTestNotificationChannel, 13, "group", false); + mService.addNotification(notification); + mService.addNotification(notificationChild); + mService.addNotification(notificationChild2); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(3)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notification.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertEquals(3, mService.getNotificationRecordCount()); + } + @Test public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() throws Exception { final NotificationRecord notification = generateNotificationRecord( mTestNotificationChannel, 1, null, true); mService.addNotification(notification); when(mSnoozeHelper.getNotification(any())).thenReturn(notification); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( notification.getKey(), 100, null); snoozeNotificationRunnable.run(); - NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 = - mService.new SnoozeNotificationRunnable( - notification.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze twice @@ -2816,19 +2877,17 @@ mService.new SnoozeNotificationRunnable( } @Test - public void testSnoozeRunnable_reSnoozeASnoozedNotificationWithGroupKey() throws Exception { + public void testSnoozeRunnable_reSnoozeASnoozedNotificationWithGroupKey() { final NotificationRecord notification = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); mService.addNotification(notification); when(mSnoozeHelper.getNotification(any())).thenReturn(notification); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( notification.getKey(), 100, null); snoozeNotificationRunnable.run(); - NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 = - mService.new SnoozeNotificationRunnable( - notification.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze twice @@ -2846,6 +2905,7 @@ public void testSnoozeRunnable_reSnoozeMultipleNotificationsWithGroupKey() throw when(mSnoozeHelper.getNotification(any())).thenReturn(notification); when(mSnoozeHelper.getNotifications( anyString(), anyString(), anyInt())).thenReturn(new ArrayList<>()); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2855,8 +2915,8 @@ mService.new SnoozeNotificationRunnable( .thenReturn(new ArrayList<>(Arrays.asList(notification, notification2))); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 = mService.new SnoozeNotificationRunnable( - notification.getKey(), 100, null); - snoozeNotificationRunnable.run(); + notification2.getKey(), 100, null); + snoozeNotificationRunnable2.run(); // snooze twice verify(mSnoozeHelper, times(4)).snooze(any(NotificationRecord.class), anyLong()); @@ -2870,6 +2930,7 @@ public void testSnoozeRunnable_snoozeNonGrouped() throws Exception { mTestNotificationChannel, 2, "group", false); mService.addNotification(grouped); mService.addNotification(nonGrouped); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2899,6 +2960,7 @@ public void testSnoozeRunnable_snoozeSummary_withChildren() throws Exception { mService.addNotification(parent); mService.addNotification(child); mService.addNotification(child2); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2920,6 +2982,7 @@ public void testSnoozeRunnable_snoozeGroupChild_fellowChildren() throws Exceptio mService.addNotification(parent); mService.addNotification(child); mService.addNotification(child2); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2945,6 +3008,7 @@ public void testSnoozeRunnable_snoozeGroupChild_onlyChildOfSummary() throws Exce mTestNotificationChannel, 2, "group", false); mService.addNotification(parent); mService.addNotification(child); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2972,6 +3036,7 @@ public void testSnoozeRunnable_snoozeGroupChild_noOthersInGroup() throws Excepti final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mService.addNotification(child); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index c2ead5f15ceb..83ba7108af7f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -15,6 +15,7 @@ */ package com.android.server.notification; +import static com.android.server.notification.SnoozeHelper.CONCURRENT_SNOOZE_LIMIT; import static com.android.server.notification.SnoozeHelper.EXTRA_KEY; import static junit.framework.Assert.assertEquals; @@ -278,6 +279,22 @@ public void testSnooze() throws Exception { UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); } + @Test + public void testSnoozeLimit() { + for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++ ) { + NotificationRecord r = getNotificationRecord("pkg", i, i+"", UserHandle.SYSTEM); + + assertTrue("cannot snooze record " + i, mSnoozeHelper.canSnooze(1)); + + if (i % 2 == 0) { + mSnoozeHelper.snooze(r, null); + } else { + mSnoozeHelper.snooze(r, 9000); + } + } + assertFalse(mSnoozeHelper.canSnooze(1)); + } + @Test public void testCancelByApp() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); From c0d719c6a2ae82f6edabed081e44a1dc72c785d2 Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Mon, 27 Jun 2022 14:01:16 -0700 Subject: [PATCH 066/208] Stop crashing the system on hitting the alarm limit Exempting the system as a runtime restart is not clearly better than extreme memory and computation pressure that can result from the originating spam. Callers in the system should guard against any spammy requests that lead them to create a lot of alarms. Test: Builds, boots and existing tests should pass. atest CtsAlarmManagerTestCases:UidCapTests atest FrameworksMockingServicesTests:AlarmManagerServiceTest Bug: 234441463 Change-Id: Id5e94d44ac9ab24870a8213ec7583da0f592a5ff (cherry picked from commit 3b9f3f4a0f5a661be65e287996cae8a4481a1453) Merged-In: Id5e94d44ac9ab24870a8213ec7583da0f592a5ff (cherry picked from commit c010da3a4649a02afe256cbf6dad475c2278059b) (cherry picked from commit 909251a2caf1a8734c7e3a43794399ab3d15ac76) Merged-In: Id5e94d44ac9ab24870a8213ec7583da0f592a5ff --- .../core/java/com/android/server/AlarmManagerService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 7cdcc01bc00d..b113a6196491 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -75,6 +75,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; import android.util.KeyValueListParser; import android.util.Log; import android.util.LongArrayQueue; @@ -1776,7 +1777,11 @@ void setImpl(int type, long triggerAtTime, long windowLength, long interval, if (DEBUG_PER_UID_LIMIT && UserHandle.isCore(callingUid)) { logAllAlarmsForUidLocked(callingUid); } - throw new IllegalStateException(errorMsg); + if (callingUid != Process.SYSTEM_UID) { + throw new IllegalStateException(errorMsg); + } else { + EventLog.writeEvent(0x534e4554, "234441463", -1, errorMsg); + } } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, directReceiver, listenerTag, flags, true, workSource, From edf37cddee76ca8bf45b81706714fbece6919cd8 Mon Sep 17 00:00:00 2001 From: Jeff DeCew Date: Mon, 18 Jul 2022 13:28:21 +0000 Subject: [PATCH 067/208] Block FullScreenIntent while device is in use if notification has a silencing GroupAlertBehavior. Bug: 231322873 Test: atest NotificationInterruptStateProviderImplTest Merged-In: Id82d20c9f1f2001400871b5381f52b40fbdf81c5 Change-Id: Id82d20c9f1f2001400871b5381f52b40fbdf81c5 (cherry picked from commit 09761a98b5bbbefa331c49a96b50b4e08dc3f8df) Merged-In: Id82d20c9f1f2001400871b5381f52b40fbdf81c5 --- ...otificationInterruptStateProviderImpl.java | 91 ++++++++++++-- ...icationInterruptStateProviderImplTest.java | 112 ++++++++++++++++++ 2 files changed, 193 insertions(+), 10 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index eeb91b548d65..8e236ea54f10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -176,9 +176,86 @@ public boolean shouldHeadsUp(NotificationEntry entry) { */ @Override public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { - return entry.getSbn().getNotification().fullScreenIntent != null - && (!shouldHeadsUp(entry) - || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); + if (entry.getSbn().getNotification().fullScreenIntent == null) { + return false; + } + + // Never show FSI when suppressed by DND + if (entry.shouldSuppressFullScreenIntent()) { + if (DEBUG) { + Log.d(TAG, "No FullScreenIntent: Suppressed by DND: " + entry.getKey()); + } + return false; + } + + // Never show FSI if importance is not HIGH + if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) { + if (DEBUG) { + Log.d(TAG, "No FullScreenIntent: Not important enough: " + entry.getKey()); + } + return false; + } + + // If the notification has suppressive GroupAlertBehavior, block FSI and warn. + StatusBarNotification sbn = entry.getSbn(); + if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { + // b/231322873: Detect and report an event when a notification has both an FSI and a + // suppressive groupAlertBehavior, and now correctly block the FSI from firing. + final int uid = entry.getSbn().getUid(); + android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior"); + if (DEBUG) { + Log.w(TAG, "No FullScreenIntent: WARNING: GroupAlertBehavior will prevent HUN: " + + entry.getKey()); + } + return false; + } + + // If the screen is off, then launch the FullScreenIntent + if (!mPowerManager.isInteractive()) { + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Device is not interactive: " + entry.getKey()); + } + return true; + } + + // If the device is currently dreaming, then launch the FullScreenIntent + if (isDreaming()) { + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Device is dreaming: " + entry.getKey()); + } + return true; + } + + // If the keyguard is showing, then launch the FullScreenIntent + if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Keyguard is showing: " + entry.getKey()); + } + return true; + } + + // If the notification should HUN, then we don't need FSI + if (shouldHeadsUp(entry)) { + if (DEBUG) { + Log.d(TAG, "No FullScreenIntent: Expected to HUN: " + entry.getKey()); + } + return false; + } + + // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI. + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Expected not to HUN: " + entry.getKey()); + } + return true; + } + + private boolean isDreaming() { + try { + return mDreamManager.isDreaming(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to query dream manager.", e); + return false; + } } private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { @@ -229,13 +306,7 @@ private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { return false; } - boolean isDreaming = false; - try { - isDreaming = mDreamManager.isDreaming(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to query dream manager.", e); - } - boolean inUse = mPowerManager.isScreenOn() && !isDreaming; + boolean inUse = mPowerManager.isScreenOn() && !isDreaming(); if (!inUse) { if (DEBUG_HEADS_UP) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index e254cd2c82a7..f4455ddb25bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -24,6 +24,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.google.common.truth.Truth.assertThat; @@ -84,6 +85,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { BatteryController mBatteryController; @Mock Handler mMockHandler; + @Mock + PendingIntent mPendingIntent; private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; @@ -399,6 +402,97 @@ public void testShouldNotHeadsUp_justLaunchedFullscreen() { assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } + @Test + public void testShouldNotFullScreen_notPendingIntent() throws RemoteException { + NotificationEntry entry = createNotification(IMPORTANCE_HIGH); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + } + + @Test + public void testShouldNotFullScreen_notHighImportance() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + } + + @Test + public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true); + when(mPowerManager.isInteractive()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + } + + @Test + public void testShouldFullScreen_notInteractive() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + } + + @Test + public void testShouldFullScreen_isDreaming() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + } + + @Test + public void testShouldFullScreen_onKeyguard() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + } + + @Test + public void testShouldNotFullScreen_willHun() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + } + + @Test + public void testShouldFullScreen_packageSnoozed() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + } + /** * Bubbles can happen. */ @@ -502,6 +596,10 @@ private NotificationEntry createNotification(int importance) { .setContentText("content text") .build(); + return createNotification(importance, n); + } + + private NotificationEntry createNotification(int importance, Notification n) { return new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") @@ -511,6 +609,20 @@ private NotificationEntry createNotification(int importance) { .build(); } + private NotificationEntry createFsiNotification(int importance, boolean silent) { + Notification n = new Notification.Builder(getContext(), "a") + .setContentTitle("title") + .setContentText("content text") + .setFullScreenIntent(mPendingIntent, true) + .setGroup("fsi") + .setGroupAlertBehavior(silent + ? Notification.GROUP_ALERT_SUMMARY + : Notification.GROUP_ALERT_ALL) + .build(); + + return createNotification(importance, n); + } + private final NotificationInterruptSuppressor mSuppressAwakeHeadsUp = new NotificationInterruptSuppressor() { From dda51b9212dda11d5bab90c2a5ed7f91d7af4eee Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Fri, 5 Aug 2022 15:14:16 +0200 Subject: [PATCH 068/208] DO NOT MERGE - Exclude TYPE_PRIVATE_PRESENTATION app visiblity These windows can only be placed on private virtual displays, and as such they should not be considered when deciding if an application has any visible windows or not. Bug:205130886 Test:Manually verified that sample from 205130886 no longer allows background activity launches Test: atest CtsActivityManagerBackgroundActivityTestCases Change-Id: I76208722bbb7a407ba1f2dc4305a28226166414d Merged-In: I76208722bbb7a407ba1f2dc4305a28226166414d (cherry picked from commit 778191bdf21661b41030f9308e095c0445dec33c) Merged-In: I76208722bbb7a407ba1f2dc4305a28226166414d --- .../android/server/wm/RootWindowContainer.java | 5 +++-- .../com/android/server/wm/WindowState.java | 18 +++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index f173c0b1caf9..e240af134435 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -571,8 +571,9 @@ private void getWindowsByName(ArrayList output, String name, int ob /** * Returns {@code true} if the callingUid has any non-toast window currently visible to the * user. Also ignores {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_STARTING}, - * since those windows don't belong to apps. - * @see WindowState#isNonToastOrStarting() + * and{@link android.view.WindowManager.LayoutParams#TYPE_PRIVATE_PRESENTATION}, as they + * should not count towards the apps visibility + * @see WindowState#isNonToastOrStartingOrPrivatePresentation() */ boolean isAnyNonToastWindowVisibleForUid(int callingUid) { final PooledPredicate p = PooledLambda.obtainPredicate( diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index faa483a48ea4..522b8d8fcce8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5812,20 +5812,24 @@ void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, } /** - * Returns {@code true} if this window is not {@link WindowManager.LayoutParams#TYPE_TOAST} - * or {@link WindowManager.LayoutParams#TYPE_APPLICATION_STARTING}, - * since this window doesn't belong to apps. + * Returns {@code true} if this window is not {@link WindowManager.LayoutParams#TYPE_TOAST}, + * {@link WindowManager.LayoutParams#TYPE_APPLICATION_STARTING} or + * {@link WindowManager.LayoutParams#TYPE_PRIVATE_PRESENTATION}, + * since those windows should not count towards the apps visibility. */ - boolean isNonToastOrStarting() { - return mAttrs.type != TYPE_TOAST && mAttrs.type != TYPE_APPLICATION_STARTING; + boolean isNonToastOrStartingOrPrivatePresentation() { + return mAttrs.type != TYPE_TOAST && mAttrs.type != TYPE_APPLICATION_STARTING + && mAttrs.type != TYPE_PRIVATE_PRESENTATION; } boolean isNonToastWindowVisibleForUid(int callingUid) { - return getOwningUid() == callingUid && isNonToastOrStarting() && isVisibleNow(); + return getOwningUid() == callingUid && isNonToastOrStartingOrPrivatePresentation() + && isVisibleNow(); } boolean isNonToastWindowVisibleForPid(int pid) { - return mSession.mPid == pid && isNonToastOrStarting() && isVisibleNow(); + return mSession.mPid == pid && isNonToastOrStartingOrPrivatePresentation() + && isVisibleNow(); } void setViewVisibility(int viewVisibility) { From 9d9e13c487a18cdd75edb8f594310aed5e0fa78c Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 16 Jun 2021 14:00:23 -0400 Subject: [PATCH 069/208] Store DND rule owners Test: uiservicestest, cts Bug: 189332346 Bug: 235823407 Change-Id: Ie546c15c25fcbd193b67cb997220f075691e9bab Merged-In: Ie546c15c25fcbd193b67cb997220f075691e9bab (cherry picked from commit 422cffbefa80c3b248458ef8ffe58d5fcf90ea3b) Merged-In: Ie546c15c25fcbd193b67cb997220f075691e9bab --- core/java/android/app/AutomaticZenRule.java | 21 ++++++- .../android/app/INotificationManager.aidl | 2 +- .../java/android/app/NotificationManager.java | 8 ++- .../service/notification/ZenModeConfig.java | 15 +++-- .../NotificationManagerService.java | 5 +- .../server/notification/ZenModeHelper.java | 19 ++++--- .../NotificationManagerServiceTest.java | 6 +- .../notification/ZenModeConfigTest.java | 55 ++++++++++++++++++- .../notification/ZenModeHelperTest.java | 2 +- 9 files changed, 106 insertions(+), 27 deletions(-) diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index 7180c01143a5..a6cd6d511caf 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -45,6 +45,7 @@ public final class AutomaticZenRule implements Parcelable { private long creationTime; private ZenPolicy mZenPolicy; private boolean mModified = false; + private String mPkg; /** * Creates an automatic zen rule. @@ -123,6 +124,7 @@ public AutomaticZenRule(Parcel source) { creationTime = source.readLong(); mZenPolicy = source.readParcelable(null); mModified = source.readInt() == ENABLED; + mPkg = source.readString(); } /** @@ -244,6 +246,20 @@ public void setConfigurationActivity(@Nullable ComponentName componentName) { this.configurationActivity = componentName; } + /** + * @hide + */ + public void setPackageName(String pkgName) { + mPkg = pkgName; + } + + /** + * @hide + */ + public String getPackageName() { + return mPkg; + } + @Override public int describeContents() { return 0; @@ -265,6 +281,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeLong(creationTime); dest.writeParcelable(mZenPolicy, 0); dest.writeInt(mModified ? ENABLED : DISABLED); + dest.writeString(mPkg); } @Override @@ -273,6 +290,7 @@ public String toString() { .append("enabled=").append(enabled) .append(",name=").append(name) .append(",interruptionFilter=").append(interruptionFilter) + .append(",pkg=").append(mPkg) .append(",conditionId=").append(conditionId) .append(",owner=").append(owner) .append(",configActivity=").append(configurationActivity) @@ -294,13 +312,14 @@ public boolean equals(Object o) { && Objects.equals(other.owner, owner) && Objects.equals(other.mZenPolicy, mZenPolicy) && Objects.equals(other.configurationActivity, configurationActivity) + && Objects.equals(other.mPkg, mPkg) && other.creationTime == creationTime; } @Override public int hashCode() { return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, - configurationActivity, mZenPolicy, mModified, creationTime); + configurationActivity, mZenPolicy, mModified, creationTime, mPkg); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index a5f987adb1a7..863fe4d5f6b1 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -203,7 +203,7 @@ interface INotificationManager void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted); AutomaticZenRule getAutomaticZenRule(String id); List getZenRules(); - String addAutomaticZenRule(in AutomaticZenRule automaticZenRule); + String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg); boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule); boolean removeAutomaticZenRule(String id); boolean removeAutomaticZenRules(String packageName); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index eef9c022fda8..553cc91fdd5f 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1072,10 +1072,12 @@ public Map getAutomaticZenRules() { List rules = service.getZenRules(); Map ruleMap = new HashMap<>(); for (ZenModeConfig.ZenRule rule : rules) { - ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component, + AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity, rule.conditionId, rule.zenPolicy, zenModeToInterruptionFilter(rule.zenMode), rule.enabled, - rule.creationTime)); + rule.creationTime); + azr.setPackageName(rule.pkg); + ruleMap.put(rule.id, azr); } return ruleMap; } catch (RemoteException e) { @@ -1116,7 +1118,7 @@ public AutomaticZenRule getAutomaticZenRule(String id) { public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { INotificationManager service = getService(); try { - return service.addAutomaticZenRule(automaticZenRule); + return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 0827fef60252..9c6e0ef4cec3 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -46,6 +46,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -159,6 +160,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ENABLED = "enabled"; private static final String RULE_ATT_SNOOZING = "snoozing"; private static final String RULE_ATT_NAME = "name"; + private static final String RULE_ATT_PKG = "pkg"; private static final String RULE_ATT_COMPONENT = "component"; private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity"; private static final String RULE_ATT_ZEN = "zen"; @@ -669,11 +671,11 @@ public static ZenRule readRuleXml(XmlPullParser parser) { rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY); - rt.pkg = (rt.component != null) - ? rt.component.getPackageName() - : (rt.configurationActivity != null) - ? rt.configurationActivity.getPackageName() - : null; + rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG); + if (rt.pkg == null) { + // backfill from component, if present. configActivity is not safe to backfill from + rt.pkg = rt.component != null ? rt.component.getPackageName() : null; + } rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); rt.condition = readConditionXml(parser); @@ -695,6 +697,9 @@ public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOExcept out.attribute(null, RULE_ATT_NAME, rule.name); } out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); + if (rule.pkg != null) { + out.attribute(null, RULE_ATT_PKG, rule.pkg); + } if (rule.component != null) { out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index dd8f3e11b575..77c440e95c08 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4506,7 +4506,7 @@ public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException { } @Override - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { + public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) { Objects.requireNonNull(automaticZenRule, "automaticZenRule is null"); Objects.requireNonNull(automaticZenRule.getName(), "Name is null"); if (automaticZenRule.getOwner() == null @@ -4515,6 +4515,7 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { "Rule must have a conditionproviderservice and/or configuration activity"); } Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null"); + checkCallerIsSameApp(pkg); if (automaticZenRule.getZenPolicy() != null && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) { throw new IllegalArgumentException("ZenPolicy is only applicable to " @@ -4522,7 +4523,7 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - return mZenModeHelper.addAutomaticZenRule(automaticZenRule, + return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, "addAutomaticZenRule"); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 49a016cb45b0..fc536a985d34 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -303,7 +303,8 @@ public AutomaticZenRule getAutomaticZenRule(String id) { return null; } - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) { + public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, + String reason) { if (!isSystemRule(automaticZenRule)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { @@ -336,7 +337,7 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reas } newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); - populateZenRule(automaticZenRule, rule, true); + populateZenRule(pkg, automaticZenRule, rule, true); newConfig.automaticRules.put(rule.id, rule); if (setConfigLocked(newConfig, reason, rule.component, true)) { return rule.id; @@ -372,7 +373,7 @@ public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZ ? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED); } - populateZenRule(automaticZenRule, rule, false); + populateZenRule(rule.pkg, automaticZenRule, rule, false); return setConfigLocked(newConfig, reason, rule.component, true); } } @@ -568,15 +569,14 @@ private ActivityInfo getActivityInfo(ComponentName configActivity) { return null; } - private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) { + private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule, + boolean isNew) { if (isNew) { rule.id = ZenModeConfig.newRuleId(); rule.creationTime = System.currentTimeMillis(); rule.component = automaticZenRule.getOwner(); rule.configurationActivity = automaticZenRule.getConfigurationActivity(); - rule.pkg = (rule.component != null) - ? rule.component.getPackageName() - : rule.configurationActivity.getPackageName(); + rule.pkg = pkg; } if (rule.enabled != automaticZenRule.isEnabled()) { @@ -593,10 +593,13 @@ private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, bo } protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) { - return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity, + AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, + rule.configurationActivity, rule.conditionId, rule.zenPolicy, NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled, rule.creationTime); + azr.setPackageName(rule.pkg); + return azr; } public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 88d7680d9236..2239769a3525 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5921,7 +5921,7 @@ public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Except zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); try { - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); fail("Zen policy only applies to priority only mode"); } catch (IllegalArgumentException e) { // yay @@ -5929,11 +5929,11 @@ public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Except rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 013a99433041..684d8cfed5b6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -188,7 +188,7 @@ public void testRuleXml() throws Exception { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = new ComponentName("a", "a"); - rule.component = new ComponentName("a", "b"); + rule.component = new ComponentName("b", "b"); rule.conditionId = new Uri.Builder().scheme("hello").build(); rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE); rule.enabled = true; @@ -198,6 +198,7 @@ public void testRuleXml() throws Exception { rule.modified = true; rule.name = "name"; rule.snoozing = true; + rule.pkg = "b"; XmlSerializer out = new FastXmlSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -213,8 +214,7 @@ public void testRuleXml() throws Exception { new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); - // read from backing component - assertEquals("a", fromXml.pkg); + assertEquals("b", fromXml.pkg); // always resets on reboot assertFalse(fromXml.snoozing); //should all match original @@ -230,6 +230,55 @@ public void testRuleXml() throws Exception { assertEquals(rule.zenMode, fromXml.zenMode); } + @Test + public void testRuleXml_pkg_component() throws Exception { + String tag = "tag"; + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = new ComponentName("a", "a"); + rule.component = new ComponentName("b", "b"); + + XmlSerializer out = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + out.setOutput(new BufferedOutputStream(baos), "utf-8"); + out.startDocument(null, true); + out.startTag(null, tag); + ZenModeConfig.writeRuleXml(rule, out); + out.endTag(null, tag); + out.endDocument(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); + assertEquals("b", fromXml.pkg); + } + + @Test + public void testRuleXml_pkg_configActivity() throws Exception { + String tag = "tag"; + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = new ComponentName("a", "a"); + + XmlSerializer out = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + out.setOutput(new BufferedOutputStream(baos), "utf-8"); + out.startDocument(null, true); + out.startTag(null, tag); + ZenModeConfig.writeRuleXml(rule, out); + out.endTag(null, tag); + out.endDocument(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); + assertNull(fromXml.pkg); + } + private ZenModeConfig getMutedRingerConfig() { ZenModeConfig config = new ZenModeConfig(); // Allow alarms, media diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index c98745e9041d..a928d4dc592f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1562,7 +1562,7 @@ public void testAddAutomaticZenRule() { new ComponentName("android", "ScheduleConditionProvider"), ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test"); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id); From e3e0e9af11180d0e43bed8fe7701aff13613fd2a Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 22 Jun 2021 13:30:37 -0400 Subject: [PATCH 070/208] Fix NPE when deleting old zen rules Test: manually revoke access for a DND app that has rules created before and after b/189332346 Bug: 191773100 Bug: 189332346 Bug: 235823407 Change-Id: I069fcc124af24227fa50b61d4fc55d6dadc7a20b Merged-In: I069fcc124af24227fa50b61d4fc55d6dadc7a20b (cherry picked from commit b4fe101e5e609f3f24ec471a35a7e0e96a416834) (cherry picked from commit 66896a01fa817c3546aacae23d326c37344984ec) Merged-In: I069fcc124af24227fa50b61d4fc55d6dadc7a20b --- .../com/android/server/notification/ZenModeHelper.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index fc536a985d34..a5d846203207 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -405,7 +405,14 @@ public boolean removeAutomaticZenRules(String packageName, String reason) { newConfig = mConfig.copy(); for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); - if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) { + String pkg = rule.pkg != null + ? rule.pkg + : (rule.component != null) + ? rule.component.getPackageName() + : (rule.configurationActivity != null) + ? rule.configurationActivity.getPackageName() + : null; + if (Objects.equals(pkg, packageName) && canManageAutomaticZenRule(rule)) { newConfig.automaticRules.removeAt(i); } } From 973784782fc63a82a53eea7dff4229a3c9b089f4 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Tue, 28 Jun 2022 15:43:33 -0400 Subject: [PATCH 071/208] Enforce zen rule limit on a package level. This means that a single package with multiple different condition providers or configuration activities will correctly have all of their zen rules associated with the same package rather than each owner/activity having their rules counted separately. Bug: 235823407 Test: ZenModeHelperTest Change-Id: I35daf9a24f546ae25a78a2d841be39072cdc5641 Merged-In: I35daf9a24f546ae25a78a2d841be39072cdc5641 (cherry picked from commit f4e69394eee569ac3ec5748094b9ce88a91c278c) (cherry picked from commit 468dd54e8b59ca494e5e3258a914447e5168ceaf) Merged-In: I35daf9a24f546ae25a78a2d841be39072cdc5641 --- .../server/notification/ZenModeHelper.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index a5d846203207..1c55762f132d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -321,7 +321,8 @@ public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner()) + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity()) + 1; - if (newRuleInstanceCount > RULE_LIMIT_PER_PACKAGE + int newPackageRuleCount = getPackageRuleCount(pkg) + 1; + if (newPackageRuleCount > RULE_LIMIT_PER_PACKAGE || (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount)) { throw new IllegalArgumentException("Rule instance limit exceeded"); } @@ -495,6 +496,23 @@ public int getCurrentInstanceCount(ComponentName cn) { return count; } + // Equivalent method to getCurrentInstanceCount, but for all rules associated with a specific + // package rather than a condition provider service or activity. + private int getPackageRuleCount(String pkg) { + if (pkg == null) { + return 0; + } + int count = 0; + synchronized (mConfig) { + for (ZenRule rule : mConfig.automaticRules.values()) { + if (pkg.equals(rule.pkg)) { + count++; + } + } + } + return count; + } + public boolean canManageAutomaticZenRule(ZenRule rule) { final int callingUid = Binder.getCallingUid(); if (callingUid == 0 || callingUid == Process.SYSTEM_UID) { From c3a8fce195257589ab092fbb3835644318c65a2e Mon Sep 17 00:00:00 2001 From: Aseem Kumar Date: Mon, 21 Mar 2022 20:35:20 -0700 Subject: [PATCH 072/208] DO NOT MERGE Move accountname and typeName length check from Account.java to AccountManagerService. Bug: 169762606 Test: atest AccountManagerServiceTest Change-Id: I80fabf3a64c55837db98ff316e7e5420129c001b (cherry picked from commit 0adcadb0b28310bac568def4da2cbaf16843bcea) Merged-In: I80fabf3a64c55837db98ff316e7e5420129c001b (cherry picked from commit aa58f99079ed8adac51b6b21faae24cb1c86262b) Merged-In: I80fabf3a64c55837db98ff316e7e5420129c001b --- .../accounts/AccountManagerService.java | 12 ++++++++++ .../accounts/AccountManagerServiceTest.java | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 90dc03642a86..e24030188c62 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1806,6 +1806,14 @@ private boolean addAccountInternal(UserAccounts accounts, Account account, Strin if (account == null) { return false; } + if (account.name != null && account.name.length() > 200) { + Log.w(TAG, "Account cannot be added - Name longer than 200 chars"); + return false; + } + if (account.type != null && account.type.length() > 200) { + Log.w(TAG, "Account cannot be added - Name longer than 200 chars"); + return false; + } if (!isLocalUnlockedUser(accounts.userId)) { Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user " + accounts.userId + " is locked. callingUid=" + callingUid); @@ -1999,6 +2007,10 @@ public void renameAccount( + ", pid " + Binder.getCallingPid()); } if (accountToRename == null) throw new IllegalArgumentException("account is null"); + if (newName != null && newName.length() > 200) { + Log.e(TAG, "renameAccount failed - account name longer than 200"); + throw new IllegalArgumentException("account name longer than 200"); + } int userId = UserHandle.getCallingUserId(); if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { String msg = String.format( diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 39a3aae767ea..ac305a93bfb8 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -36,6 +36,7 @@ import android.accounts.IAccountManagerResponse; import android.app.AppOpsManager; import android.app.INotificationManager; +import android.app.PropertyInvalidatedCache; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.BroadcastReceiver; @@ -132,6 +133,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { protected void setUp() throws Exception { MockitoAnnotations.initMocks(this); + PropertyInvalidatedCache.disableForTestMode(); + when(mMockPackageManager.checkSignatures(anyInt(), anyInt())) .thenReturn(PackageManager.SIGNATURE_MATCH); final UserInfo ui = new UserInfo(UserHandle.USER_SYSTEM, "user0", 0); @@ -241,6 +244,25 @@ public void testCheckAddAccount() throws Exception { assertEquals(a31, accounts[1]); } + @SmallTest + public void testCheckAddAccountLongName() throws Exception { + unlockSystemUser(); + String longString = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaa"; + Account a11 = new Account(longString, AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); + + mAms.addAccountExplicitly(a11, /* password= */ "p11", /* extras= */ null); + + String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE}; + when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list); + Account[] accounts = mAms.getAccountsAsUser(null, + UserHandle.getCallingUserId(), mContext.getOpPackageName()); + assertEquals(0, accounts.length); + } + + @SmallTest public void testPasswords() throws Exception { unlockSystemUser(); From 902d3bdfe0af42fa2376009d5c7e91cad48e739a Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Thu, 23 Jun 2022 14:27:43 -0700 Subject: [PATCH 073/208] switch TelecomManager List getters to ParceledListSlice It was shown that given a large phoneAccountHandles that are over 1 mb, a TransactionTooLarge exception can be silently thrown causing an empty list to be returned. In order to prevent this behavior, all Lists that return a PhoneAccountHandle or PhoneAccount have been switched to ParceledListSlice. bug: 236263294 Test: atest android.telecom.cts.PhoneAccountRegistrarTest #testRegisterPhoneAccountHandleWithFieldOverLimit Change-Id: I025245b2a6f8cfaca86f268851a9d8f0817e07dd Merged-In: I025245b2a6f8cfaca86f268851a9d8f0817e07dd (cherry picked from commit f5ab5186dac6dd710693716c13413f30791be0e9) Merged-In: I025245b2a6f8cfaca86f268851a9d8f0817e07dd --- telecomm/java/android/telecom/TelecomManager.java | 13 +++++++------ .../android/internal/telecom/ITelecomService.aidl | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index c160023ca944..19a88001428d 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1153,7 +1153,7 @@ public List getPhoneAccountsSupportingScheme(String uriSchem try { if (isServiceConnected()) { return getTelecomService().getPhoneAccountsSupportingScheme(uriScheme, - mContext.getOpPackageName()); + mContext.getOpPackageName()).getList(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsSupportingScheme", e); @@ -1196,7 +1196,7 @@ public List getSelfManagedPhoneAccounts() { try { if (isServiceConnected()) { return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag()).getList(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e); @@ -1222,7 +1222,7 @@ public List getSelfManagedPhoneAccounts() { try { if (isServiceConnected()) { return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts, - mContext.getOpPackageName(), mContext.getAttributionTag()); + mContext.getOpPackageName(), mContext.getAttributionTag()).getList(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" + @@ -1242,7 +1242,8 @@ public List getSelfManagedPhoneAccounts() { public List getPhoneAccountsForPackage() { try { if (isServiceConnected()) { - return getTelecomService().getPhoneAccountsForPackage(mContext.getPackageName()); + return getTelecomService() + .getPhoneAccountsForPackage(mContext.getPackageName()).getList(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getPhoneAccountsForPackage", e); @@ -1296,7 +1297,7 @@ public int getAllPhoneAccountsCount() { public List getAllPhoneAccounts() { try { if (isServiceConnected()) { - return getTelecomService().getAllPhoneAccounts(); + return getTelecomService().getAllPhoneAccounts().getList(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccounts", e); @@ -1314,7 +1315,7 @@ public List getAllPhoneAccounts() { public List getAllPhoneAccountHandles() { try { if (isServiceConnected()) { - return getTelecomService().getAllPhoneAccountHandles(); + return getTelecomService().getAllPhoneAccountHandles().getList(); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getAllPhoneAccountHandles", e); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index dee5a98e33e9..d27093ad0133 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -23,6 +23,7 @@ import android.telecom.PhoneAccountHandle; import android.net.Uri; import android.os.Bundle; import android.telecom.PhoneAccount; +import android.content.pm.ParceledListSlice; /** * Interface used to interact with Telecom. Mostly this is used by TelephonyManager for passing @@ -56,25 +57,25 @@ interface ITelecomService { /** * @see TelecomServiceImpl#getCallCapablePhoneAccounts */ - List getCallCapablePhoneAccounts( + ParceledListSlice getCallCapablePhoneAccounts( boolean includeDisabledAccounts, String callingPackage, String callingFeatureId); /** * @see TelecomServiceImpl#getSelfManagedPhoneAccounts */ - List getSelfManagedPhoneAccounts(String callingPackage, + ParceledListSlice getSelfManagedPhoneAccounts(String callingPackage, String callingFeatureId); /** * @see TelecomManager#getPhoneAccountsSupportingScheme */ - List getPhoneAccountsSupportingScheme(in String uriScheme, + ParceledListSlice getPhoneAccountsSupportingScheme(in String uriScheme, String callingPackage); /** * @see TelecomManager#getPhoneAccountsForPackage */ - List getPhoneAccountsForPackage(in String packageName); + ParceledListSlice getPhoneAccountsForPackage(in String packageName); /** * @see TelecomManager#getPhoneAccount @@ -89,12 +90,12 @@ interface ITelecomService { /** * @see TelecomManager#getAllPhoneAccounts */ - List getAllPhoneAccounts(); + ParceledListSlice getAllPhoneAccounts(); /** * @see TelecomManager#getAllPhoneAccountHandles */ - List getAllPhoneAccountHandles(); + ParceledListSlice getAllPhoneAccountHandles(); /** * @see TelecomServiceImpl#getSimCallManager From ce6b5f925e1df482f31c906214436a49a5ac6b99 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Tue, 2 Aug 2022 03:33:39 +0000 Subject: [PATCH 074/208] [RESTRICT AUTOMERGE] Do not send new Intent to non-exported activity when navigateUpTo The new Intent was delivered to a non-exported activity while #navigateUpTo was called from an Activity of a different uid. Bug: 238605611 Test: atest StartActivityTests Change-Id: I854dd825bfd9a2c08851980d480d1f3a177af6cf Merged-In: I854dd825bfd9a2c08851980d480d1f3a177af6cf (cherry picked from commit 834812c423f10deb95953d41a7007d4cba78f1ec) Merged-In: I854dd825bfd9a2c08851980d480d1f3a177af6cf --- .../com/android/server/wm/ActivityStack.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 6d452c39eccc..071e9a56e5f5 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2404,7 +2404,23 @@ boolean navigateUpTo(ActivityRecord srec, Intent destIntent, NeededUriGrants des parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK || parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP || (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { - parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName); + boolean abort; + try { + abort = !mStackSupervisor.checkStartAnyActivityPermission(destIntent, + parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(), + callingUid, srec.info.packageName, null /* callingFeatureId */, + false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app, + null /* resultRecord */, null /* resultRootTask */); + } catch (SecurityException e) { + abort = true; + } + if (abort) { + android.util.EventLog.writeEvent(0x534e4554, "238605611", callingUid, ""); + foundParentInTask = false; + } else { + parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, + srec.packageName); + } } else { try { ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( From 50ec233b7bc8b8b8bfed65d6126dd59b167ed68e Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Fri, 12 Aug 2022 11:40:41 -0700 Subject: [PATCH 075/208] Do not send AccessibilityEvent if notification is for different user. Bug: 237540408 Test: BuzzBeepBlinkTest#testA11yCrossUserEventNotSent Change-Id: I62a875e26e214847ec72ce3c41b4f2fa8e597e07 (cherry picked from commit a367c0a16a9070ed6bee3028ac5bbc967773ee8f) Merged-In: I62a875e26e214847ec72ce3c41b4f2fa8e597e07 --- .../notification/NotificationManagerService.java | 3 ++- .../server/notification/BuzzBeepBlinkTest.java | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 77c440e95c08..60d7ec51ca09 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7075,7 +7075,8 @@ int buzzBeepBlinkLocked(NotificationRecord record) { && (record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_STATUS_BAR) != 0; if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN - && !suppressedByDnd) { + && !suppressedByDnd + && isNotificationForCurrentUser(record)) { sendAccessibilityEvent(record); sentAccessibilityEvent = true; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index a21bfda58529..9bf8758c6a8b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -1281,6 +1281,21 @@ public void testA11yQuietUpdate() throws Exception { verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); } + @Test + public void testA11yCrossUserEventNotSent() throws Exception { + final Notification n = new Builder(getContext(), "test") + .setSmallIcon(android.R.drawable.sym_def_app_icon).build(); + int userId = mUser.getIdentifier() + 1; + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid, + mPid, n, UserHandle.of(userId), null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, + new NotificationChannel("test", "test", IMPORTANCE_HIGH)); + + mService.buzzBeepBlinkLocked(r); + + verify(mAccessibilityService, never()).sendAccessibilityEvent(any(), anyInt()); + } + @Test public void testLightsScreenOn() { mService.mScreenOn = true; From 8d96665f69de3631562cf642cfc23ee4e995c072 Mon Sep 17 00:00:00 2001 From: Ganesh Olekar Date: Thu, 30 Jun 2022 00:20:15 +0000 Subject: [PATCH 076/208] DO NOT MERGE Fix auto-grant of AR runtime permission if device is upgrading from pre-Q Test: Manually install app apks targeting Q and verifying that AR permission is not auto-granted Test: atest ActivityRecognitionPermissionTest Bug: 210065877 Change-Id: I7004055c9573d17f31255c2b1adee1e0aeeb238f (cherry picked from commit f11f26c0121152ffa5c8493ebbedb9fd369ec6c4) Merged-In: I7004055c9573d17f31255c2b1adee1e0aeeb238f --- .../content/pm/PackageManagerInternal.java | 6 ++++ .../server/pm/PackageManagerService.java | 5 +++ .../permission/PermissionManagerService.java | 34 +++++++++++-------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index b241bd16d3ee..83801ff0534c 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -998,4 +998,10 @@ public abstract void setIntegrityVerificationResult(int verificationId, * Returns {@code true} if the package is suspending any packages for the user. */ public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId); + + /** + * Get installed SDK version of the package + * @param pkg package for which to retrieve the installed sdk version + */ + public abstract int getInstalledSdkVersion(AndroidPackage pkg); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 71e665122167..4c5e966f54c0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -25295,6 +25295,11 @@ public void unsuspendForSuspendingPackage(final String packageName, int affected public boolean isSuspendingAnyPackages(String suspendingPackage, int userId) { return PackageManagerService.this.isSuspendingAnyPackages(suspendingPackage, userId); } + + @Override + public int getInstalledSdkVersion(AndroidPackage pkg) { + return PackageManagerService.this.getSettingsVersionForPackage(pkg).sdkVersion; + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 8d2363b6e831..663f6ebcd73a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2755,7 +2755,7 @@ private void restorePermissionState(@NonNull AndroidPackage pkg, boolean replace // uids the original and new state are the same object if (!origPermissions.hasRequestedPermission(permName) && (pkg.getImplicitPermissions().contains(permName) - || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) { + || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) { if (pkg.getImplicitPermissions().contains(permName)) { // If permName is an implicit permission, try to auto-grant newImplicitPermissions.add(permName); @@ -2771,21 +2771,27 @@ private void restorePermissionState(@NonNull AndroidPackage pkg, boolean replace // or has updated its target SDK and AR is no longer implicit to it. // This is a compatibility workaround for apps when AR permission was // split in Q. - final List permissionList = - getSplitPermissions(); - int numSplitPerms = permissionList.size(); - for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { - SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum); - String splitPermName = sp.getSplitPermission(); - if (sp.getNewPermissions().contains(permName) - && origPermissions.hasInstallPermission(splitPermName)) { - upgradedActivityRecognitionPermission = splitPermName; - newImplicitPermissions.add(permName); + // b/210065877: Check that the installed version is pre Q to auto-grant in + // case of OS update + if (mPackageManagerInt.getInstalledSdkVersion(pkg) + < Build.VERSION_CODES.Q) { + final List permissionList = + getSplitPermissions(); + int numSplitPerms = permissionList.size(); + for (int splitPermNum = 0; splitPermNum < numSplitPerms; + splitPermNum++) { + SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum); + String splitPermName = sp.getSplitPermission(); + if (sp.getNewPermissions().contains(permName) + && origPermissions.hasInstallPermission(splitPermName)) { + upgradedActivityRecognitionPermission = splitPermName; + newImplicitPermissions.add(permName); - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " + friendlyName); + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, permName + " is newly added for " + friendlyName); + } + break; } - break; } } } From 43192d28fa7dbb413e60636ddb05fed316d1325c Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Thu, 25 Aug 2022 16:23:12 -0400 Subject: [PATCH 077/208] Check rule package name in ZenModeHelper.addAutomaticRule instead of checking that of the configuration activity, which is potentially spoofable. The package name is verified to be the same app as the caller by NMS. This change removes isSystemRule (called only once) in favor of checking the provided package name directly. Bug: 242537431 Test: ZenModeHelperTest, manual by verifying via provided exploit apk Change-Id: Ic7f350618c26a613df455a4128c9195f4b424a4d Merged-In: Ic7f350618c26a613df455a4128c9195f4b424a4d (cherry picked from commit 3201baad70443854e7630a5c301f8bde573a43f7) Merged-In: Ic7f350618c26a613df455a4128c9195f4b424a4d --- .../server/notification/ZenModeHelper.java | 7 +------ .../server/notification/ZenModeHelperTest.java | 16 +++++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 1c55762f132d..c96aac6e9914 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -305,7 +305,7 @@ public AutomaticZenRule getAutomaticZenRule(String id) { public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, String reason) { - if (!isSystemRule(automaticZenRule)) { + if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { component = getActivityInfo(automaticZenRule.getConfigurationActivity()); @@ -554,11 +554,6 @@ protected void updateDefaultZenRules() { } } - private boolean isSystemRule(AutomaticZenRule rule) { - return rule.getOwner() != null - && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName()); - } - private ServiceInfo getServiceInfo(ComponentName owner) { Intent queryIntent = new Intent(); queryIntent.setComponent(owner); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index a928d4dc592f..88e2c02f63b9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1576,27 +1576,29 @@ public void testAddAutomaticZenRule() { } @Test - public void testAddAutomaticZenRule_beyondSystemLimit() { + public void testAddAutomaticZenRule_claimedSystemOwner() { + // Make sure anything that claims to have a "system" owner but not actually part of the + // system package still gets limited on number of rules for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { ScheduleInfo si = new ScheduleInfo(); si.startHour = i; AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, - null, - new ComponentName("android", "ScheduleConditionProvider"), + new ComponentName("android", "ScheduleConditionProvider" + i), + null, // configuration activity ZenModeConfig.toScheduleConditionId(si), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); assertNotNull(id); } try { AutomaticZenRule zenRule = new AutomaticZenRule("name", - null, - new ComponentName("android", "ScheduleConditionProvider"), + new ComponentName("android", "ScheduleConditionProviderFinal"), + null, // configuration activity ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); fail("allowed too many rules to be created"); } catch (IllegalArgumentException e) { // yay From 38898034f3edf4a7ef67969bc66d3b48eb7ceac6 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Mon, 29 Aug 2022 17:40:14 -0400 Subject: [PATCH 078/208] Trim any long string inputs that come in to AutomaticZenRule This change both prevents any rules from being unable to be written to disk and also avoids risk of running out of memory while handling all the zen rules. Bug: 242703460 Bug: 242703505 Bug: 242703780 Bug: 242704043 Bug: 243794204 Test: cts AutomaticZenRuleTest; atest android.app.AutomaticZenRuleTest; manually confirmed each exploit example either saves the rule successfully with a truncated string (in the case of name & conditionId) or may fail to save the rule at all (if the owner/configactivity is invalid). Additionally ran the memory-exhausting PoC without device crashes. Change-Id: I110172a43f28528dd274b3b346eb29c3796ff2c6 Merged-In: I110172a43f28528dd274b3b346eb29c3796ff2c6 (cherry picked from commit de172ba0d434c940be9e2aad8685719731ab7da2) (cherry picked from commit 1fcd99f5aa3490431154d1e80b854aafab821345) Merged-In: I110172a43f28528dd274b3b346eb29c3796ff2c6 --- core/java/android/app/AutomaticZenRule.java | 58 +++++-- .../src/android/app/AutomaticZenRuleTest.java | 153 ++++++++++++++++++ 2 files changed, 201 insertions(+), 10 deletions(-) create mode 100644 core/tests/coretests/src/android/app/AutomaticZenRuleTest.java diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index a6cd6d511caf..37b336382769 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -47,6 +47,13 @@ public final class AutomaticZenRule implements Parcelable { private boolean mModified = false; private String mPkg; + /** + * The maximum string length for any string contained in this automatic zen rule. This pertains + * both to fields in the rule itself (such as its name) and items with sub-fields. + * @hide + */ + public static final int MAX_STRING_LENGTH = 1000; + /** * Creates an automatic zen rule. * @@ -93,10 +100,10 @@ public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner, @Nullable ComponentName configurationActivity, @NonNull Uri conditionId, @Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) { - this.name = name; - this.owner = owner; - this.configurationActivity = configurationActivity; - this.conditionId = conditionId; + this.name = getTrimmedString(name); + this.owner = getTrimmedComponentName(owner); + this.configurationActivity = getTrimmedComponentName(configurationActivity); + this.conditionId = getTrimmedUri(conditionId); this.interruptionFilter = interruptionFilter; this.enabled = enabled; this.mZenPolicy = policy; @@ -115,12 +122,12 @@ public AutomaticZenRule(String name, ComponentName owner, ComponentName configur public AutomaticZenRule(Parcel source) { enabled = source.readInt() == ENABLED; if (source.readInt() == ENABLED) { - name = source.readString(); + name = getTrimmedString(source.readString()); } interruptionFilter = source.readInt(); conditionId = source.readParcelable(null); - owner = source.readParcelable(null); - configurationActivity = source.readParcelable(null); + owner = getTrimmedComponentName(source.readParcelable(null)); + configurationActivity = getTrimmedComponentName(source.readParcelable(null)); creationTime = source.readLong(); mZenPolicy = source.readParcelable(null); mModified = source.readInt() == ENABLED; @@ -196,7 +203,7 @@ public long getCreationTime() { * Sets the representation of the state that causes this rule to become active. */ public void setConditionId(Uri conditionId) { - this.conditionId = conditionId; + this.conditionId = getTrimmedUri(conditionId); } /** @@ -211,7 +218,7 @@ public void setInterruptionFilter(@InterruptionFilter int interruptionFilter) { * Sets the name of this rule. */ public void setName(String name) { - this.name = name; + this.name = getTrimmedString(name); } /** @@ -243,7 +250,7 @@ public void setZenPolicy(ZenPolicy zenPolicy) { * that are not backed by {@link android.service.notification.ConditionProviderService}. */ public void setConfigurationActivity(@Nullable ComponentName componentName) { - this.configurationActivity = componentName; + this.configurationActivity = getTrimmedComponentName(componentName); } /** @@ -333,4 +340,35 @@ public AutomaticZenRule[] newArray(int size) { return new AutomaticZenRule[size]; } }; + + /** + * If the package or class name of the provided ComponentName are longer than MAX_STRING_LENGTH, + * return a trimmed version that truncates each of the package and class name at the max length. + */ + private static ComponentName getTrimmedComponentName(ComponentName cn) { + if (cn == null) return null; + return new ComponentName(getTrimmedString(cn.getPackageName()), + getTrimmedString(cn.getClassName())); + } + + /** + * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH. + */ + private static String getTrimmedString(String input) { + if (input != null && input.length() > MAX_STRING_LENGTH) { + return input.substring(0, MAX_STRING_LENGTH); + } + return input; + } + + /** + * Returns a truncated copy of the Uri by trimming the string representation to the maximum + * string length. + */ + private static Uri getTrimmedUri(Uri input) { + if (input != null && input.toString().length() > MAX_STRING_LENGTH) { + return Uri.parse(getTrimmedString(input.toString())); + } + return input; + } } diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java new file mode 100644 index 000000000000..282fdad294eb --- /dev/null +++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; + +import android.content.ComponentName; +import android.net.Uri; +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.google.common.base.Strings; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class AutomaticZenRuleTest { + private static final String CLASS = "android.app.AutomaticZenRule"; + + @Test + public void testLongFields_inConstructor() { + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + + // test both variants where there's an owner, and where there's a configuration activity + AutomaticZenRule rule1 = new AutomaticZenRule( + longString, // name + new ComponentName("pkg", longString), // owner + null, // configuration activity + longUri, // conditionId + null, // zen policy + 0, // interruption filter + true); // enabled + + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, rule1.getName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + rule1.getConditionId().toString().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, rule1.getOwner().getClassName().length()); + + AutomaticZenRule rule2 = new AutomaticZenRule( + longString, // name + null, // owner + new ComponentName(longString, "SomeClassName"), // configuration activity + longUri, // conditionId + null, // zen policy + 0, // interruption filter + false); // enabled + + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, rule2.getName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + rule2.getConditionId().toString().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + rule2.getConfigurationActivity().getPackageName().length()); + } + + @Test + public void testLongFields_inSetters() { + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + + AutomaticZenRule rule = new AutomaticZenRule( + "sensible name", + new ComponentName("pkg", "ShortClass"), + null, + Uri.parse("uri://short"), + null, 0, true); + + rule.setName(longString); + rule.setConditionId(longUri); + rule.setConfigurationActivity(new ComponentName(longString, longString)); + + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, rule.getName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + rule.getConditionId().toString().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + rule.getConfigurationActivity().getPackageName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + rule.getConfigurationActivity().getClassName().length()); + } + + @Test + public void testLongInputsFromParcel() { + // Create a rule with long fields, set directly via reflection so that we can confirm that + // a rule with too-long fields that comes in via a parcel has its fields truncated directly. + AutomaticZenRule rule = new AutomaticZenRule( + "placeholder", + new ComponentName("place", "holder"), + null, + Uri.parse("uri://placeholder"), + null, 0, true); + + try { + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + Field name = Class.forName(CLASS).getDeclaredField("name"); + name.setAccessible(true); + name.set(rule, longString); + Field conditionId = Class.forName(CLASS).getDeclaredField("conditionId"); + conditionId.setAccessible(true); + conditionId.set(rule, longUri); + Field owner = Class.forName(CLASS).getDeclaredField("owner"); + owner.setAccessible(true); + owner.set(rule, new ComponentName(longString, longString)); + Field configActivity = Class.forName(CLASS).getDeclaredField("configurationActivity"); + configActivity.setAccessible(true); + configActivity.set(rule, new ComponentName(longString, longString)); + } catch (NoSuchFieldException e) { + fail(e.toString()); + } catch (ClassNotFoundException e) { + fail(e.toString()); + } catch (IllegalAccessException e) { + fail(e.toString()); + } + + Parcel parcel = Parcel.obtain(); + rule.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + AutomaticZenRule fromParcel = new AutomaticZenRule(parcel); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, fromParcel.getName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + fromParcel.getConditionId().toString().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + fromParcel.getConfigurationActivity().getPackageName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + fromParcel.getConfigurationActivity().getClassName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + fromParcel.getOwner().getPackageName().length()); + assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, + fromParcel.getOwner().getClassName().length()); + } +} From a01a298fde349033adba7bf3702168c0717f617b Mon Sep 17 00:00:00 2001 From: Ivan Chiang Date: Mon, 18 Jul 2022 15:28:46 +0800 Subject: [PATCH 079/208] [RESTRICT AUTOMERGE] Check permission for VoiceInteraction The service must have the CAPTURE_AUDIO_HOTWORD permission to access AlwaysOnHotwordDetector. If it doesn't have the permission, return STATE_HARDWARE_UNAVAILABLE state. If it is not granted the RECORD_AUDIO permisison, it also can't start to recognize the audio. Test: manual Test: atest CtsVoiceInteractionTestCases Test: atest CtsAssistTestCases Bug: 229793943 Change-Id: I7d0f8d2f6af4bc4210060f0a44469db2afc7a1bb (cherry picked from commit 525690ce16c1c7a48b7880897a4349e2dda0ca09) Merged-In: I7d0f8d2f6af4bc4210060f0a44469db2afc7a1bb --- .../voice/AlwaysOnHotwordDetector.java | 57 ++++++++++++++++++- .../voice/VoiceInteractionService.java | 2 +- .../VoiceInteractionManagerService.java | 23 ++++++++ 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 6f941121771e..b956d1918f7e 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -16,12 +16,14 @@ package android.service.voice; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.KeyphraseMetadata; @@ -232,8 +234,10 @@ public class AlwaysOnHotwordDetector { private final Callback mExternalCallback; private final Object mLock = new Object(); private final Handler mHandler; + private final Context mContext; private int mAvailability = STATE_NOT_READY; + private boolean mIsGrantedHotwordPermission; /** * A ModelParamRange is a representation of supported parameter range for a @@ -408,23 +412,37 @@ public static abstract class Callback { public abstract void onRecognitionResumed(); } + private static boolean hasHotwordPermission(Context context) { + return context.checkSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD) + == PackageManager.PERMISSION_GRANTED; + } + + private static boolean hasRecordAudioPermission(Context context) { + return context.checkSelfPermission(Manifest.permission.RECORD_AUDIO) + == PackageManager.PERMISSION_GRANTED; + } + /** + * @param context The context to check permission * @param text The keyphrase text to get the detector for. * @param locale The java locale for the detector. * @param callback A non-null Callback for receiving the recognition events. + * @param keyphraseEnrollmentInfo The Enrollment info of key phrase * @param modelManagementService A service that allows management of sound models. * @hide */ - public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback, + public AlwaysOnHotwordDetector(Context context, String text, Locale locale, Callback callback, KeyphraseEnrollmentInfo keyphraseEnrollmentInfo, IVoiceInteractionManagerService modelManagementService) { mText = text; + mContext = context; mLocale = locale; mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo; mExternalCallback = callback; mHandler = new MyHandler(); mInternalCallback = new SoundTriggerListener(mHandler); mModelManagementService = modelManagementService; + mIsGrantedHotwordPermission = hasHotwordPermission(mContext); new RefreshAvailabiltyTask().execute(); } @@ -477,6 +495,11 @@ private int getSupportedRecognitionModesLocked() { @AudioCapabilities public int getSupportedAudioCapabilities() { if (DBG) Slog.d(TAG, "getSupportedAudioCapabilities()"); + + if (!mIsGrantedHotwordPermission) { + return 0; + } + synchronized (mLock) { return getSupportedAudioCapabilitiesLocked(); } @@ -515,6 +538,12 @@ private int getSupportedAudioCapabilitiesLocked() { */ public boolean startRecognition(@RecognitionFlags int recognitionFlags) { if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")"); + + if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { + throw new IllegalStateException("Must have the RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD " + + "permissions to access the detector."); + } + synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("startRecognition called on an invalid detector"); @@ -545,6 +574,12 @@ public boolean startRecognition(@RecognitionFlags int recognitionFlags) { */ public boolean stopRecognition() { if (DBG) Slog.d(TAG, "stopRecognition()"); + + if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { + throw new IllegalStateException("Must have the RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD " + + "permissions to access the detector."); + } + synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("stopRecognition called on an invalid detector"); @@ -582,6 +617,10 @@ public int setParameter(@ModelParams int modelParam, int value) { Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")"); } + if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { + return SoundTrigger.STATUS_INVALID_OPERATION; + } + synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("setParameter called on an invalid detector"); @@ -609,6 +648,10 @@ public int getParameter(@ModelParams int modelParam) { Slog.d(TAG, "getParameter(" + modelParam + ")"); } + if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { + return MODEL_PARAM_THRESHOLD_FACTOR; + } + synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("getParameter called on an invalid detector"); @@ -634,6 +677,10 @@ public ModelParamRange queryParameter(@ModelParams int modelParam) { Slog.d(TAG, "queryParameter(" + modelParam + ")"); } + if (!mIsGrantedHotwordPermission || !hasRecordAudioPermission(mContext)) { + return null; + } + synchronized (mLock) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("queryParameter called on an invalid detector"); @@ -742,8 +789,8 @@ void invalidate() { */ void onSoundModelsChanged() { synchronized (mLock) { - if (mAvailability == STATE_INVALID - || mAvailability == STATE_HARDWARE_UNAVAILABLE) { + if (mAvailability == STATE_INVALID || mAvailability == STATE_HARDWARE_UNAVAILABLE + || !hasRecordAudioPermission(mContext)) { Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config"); return; } @@ -962,6 +1009,10 @@ public Void doInBackground(Void... params) { * @return The initial availability without checking the enrollment status. */ private int internalGetInitialAvailability() { + if (!mIsGrantedHotwordPermission) { + return STATE_HARDWARE_UNAVAILABLE; + } + synchronized (mLock) { // This detector has already been invalidated. if (mAvailability == STATE_INVALID) { diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 45d3465fdae8..a56d40790dbf 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -318,7 +318,7 @@ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector( synchronized (mLock) { // Allow only one concurrent recognition via the APIs. safelyShutdownHotwordDetector(); - mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback, + mHotwordDetector = new AlwaysOnHotwordDetector(this, keyphrase, locale, callback, mKeyphraseEnrollmentInfo, mSystemService); } return mHotwordDetector; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 0abab087b6d8..d70166f02ef9 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1094,6 +1094,9 @@ public KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, @Override public ModuleProperties getDspModuleProperties() { + // Allow the call if it is granted CAPTURE_AUDIO_HOTWORD. + enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); + // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); @@ -1110,6 +1113,9 @@ public ModuleProperties getDspModuleProperties() { @Override public int startRecognition(int keyphraseId, String bcp47Locale, IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) { + // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. + enforceAlwaysOnHotwordPermissions(); + // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); @@ -1145,6 +1151,9 @@ public int startRecognition(int keyphraseId, String bcp47Locale, @Override public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) { + // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. + enforceAlwaysOnHotwordPermissions(); + // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); @@ -1160,6 +1169,9 @@ public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) @Override public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) { + // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. + enforceAlwaysOnHotwordPermissions(); + // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); @@ -1175,6 +1187,9 @@ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) @Override public int getParameter(int keyphraseId, @ModelParams int modelParam) { + // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. + enforceAlwaysOnHotwordPermissions(); + // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); @@ -1191,6 +1206,9 @@ public int getParameter(int keyphraseId, @ModelParams int modelParam) { @Override @Nullable public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) { + // Allow the call if it is granted RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. + enforceAlwaysOnHotwordPermissions(); + // Allow the call if this is the current voice interaction service. synchronized (this) { enforceIsCurrentVoiceInteractionService(); @@ -1454,6 +1472,11 @@ private boolean isCallerHoldingPermission(String permission) { == PackageManager.PERMISSION_GRANTED; } + private void enforceAlwaysOnHotwordPermissions() { + enforceCallingPermission(Manifest.permission.RECORD_AUDIO); + enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); + } + private void enforceCallingPermission(String permission) { if (!isCallerHoldingPermission(permission)) { throw new SecurityException("Caller does not hold the permission " + permission); From 3a96ce77f22ebae7deef8c4b7379a18de81707ce Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Tue, 6 Sep 2022 16:14:16 -0400 Subject: [PATCH 080/208] Fix system zen rules by using owner package name if caller is system Previously were unable to add new zen rules because rules added via the settings pages were getting registered under package "com.android.settings", which then were not considered "system rules". These rules should have package android, so when we can trust the caller (via checking that the caller is system) we should be taking the package name from the owner of the rule. Bug: 245236706 Bug: 242537431 Test: NMSTest; manual Change-Id: Id69b671592396ac3304862dadbe73de328a8e27a Merged-In: Id69b671592396ac3304862dadbe73de328a8e27a (cherry picked from commit 0b262d0ae5cd30a8411ca3fafd7919d72b2fa464) Merged-In: Id69b671592396ac3304862dadbe73de328a8e27a --- .../NotificationManagerService.java | 11 +++++- .../NotificationManagerServiceTest.java | 37 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 60d7ec51ca09..1cfaf960271f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4523,7 +4523,16 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, + // If the caller is system, take the package name from the rule's owner rather than + // from the caller's package. + String rulePkg = pkg; + if (isCallingUidSystem()) { + if (automaticZenRule.getOwner() != null) { + rulePkg = automaticZenRule.getOwner().getPackageName(); + } + } + + return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule, "addAutomaticZenRule"); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 2239769a3525..f5cf23125c1b 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5936,6 +5936,43 @@ public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Except mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } + @Test + public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception { + mService.isSystemUid = true; + ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mockZenModeHelper); + ComponentName owner = new ComponentName("android", "ProviderName"); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule, "com.android.settings"); + + // verify that zen mode helper gets passed in a package name of "android" + verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); + } + + @Test + public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception { + mService.isSystemUid = false; + ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mockZenModeHelper); + ComponentName owner = new ComponentName("android", "ProviderName"); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule, "another.package"); + + // verify that zen mode helper gets passed in the package name from the arg, not the owner + verify(mockZenModeHelper).addAutomaticZenRule( + eq("another.package"), eq(rule), anyString()); + } + @Test public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { try { From d192ab48d0c7096693feef81b5b5bb4221007480 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Thu, 18 Aug 2022 12:04:43 +0000 Subject: [PATCH 081/208] [DO NOT MERGE] Do not dismiss keyguard after SIM PUK unlock After PUK unlock, multiple calls to KeyguardSecurityContainerController#dismiss() were being called from the KeyguardSimPukViewController, which begins the transition to the next security screen, if any. At the same time, other parts of the system, also listening to SIM events, recognize the PUK unlock and call KeyguardSecurityContainer#showSecurityScreen, which updates which security method comes next. After boot, this should be one of PIN, Password, Pattern, assuming they have a security method. If one of the first dismiss() calls comes AFTER the security method changes, this is incorrectly recognized by the code as a successful PIN/pattern/password unlock. This causes the keyguard to be marked as done, causing screen flickers and incorrect system state. The solution: every call to dismiss() should include a new parameter for the security method used. If there is a difference between this parameter and the current value in KeyguardSecurityContainerCallback, ignore the request, as the system state has changed. Bug: 218500036 Test: atest KeyguardSecurityContainerTest Merged-In: I7c8714a177bc85fbce92f6e8fe911f74ca2ac243 Change-Id: I30226bc7b5eda9480d471b35fe81e106b0491ff8 (cherry picked from commit c27cf661a6b14849bd13ba69b5ee40c9066fcf2c) Merged-In: I30226bc7b5eda9480d471b35fe81e106b0491ff8 --- .../AdminSecondaryLockScreenController.java | 3 +- .../keyguard/KeyguardAbsKeyInputView.java | 4 ++- .../android/keyguard/KeyguardHostView.java | 11 +++--- .../com/android/keyguard/KeyguardPINView.java | 6 ++++ .../keyguard/KeyguardPasswordView.java | 6 ++++ .../android/keyguard/KeyguardPatternView.java | 3 +- .../keyguard/KeyguardSecurityCallback.java | 9 +++-- .../keyguard/KeyguardSecurityContainer.java | 34 +++++++++++++------ .../android/keyguard/KeyguardSimPinView.java | 9 ++++- .../android/keyguard/KeyguardSimPukView.java | 13 +++++-- ...dminSecondaryLockScreenControllerTest.java | 3 +- 11 files changed, 76 insertions(+), 25 deletions(-) diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java index e99245fa438f..f9e4340b4fc8 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -33,6 +33,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import java.util.NoSuchElementException; @@ -204,7 +205,7 @@ private void dismiss(int userId) { hide(); if (mKeyguardCallback != null) { mKeyguardCallback.dismiss(/* securityVerified= */ true, userId, - /* bypassSecondaryLockScreen= */true); + /* bypassSecondaryLockScreen= */true, SecurityMode.Invalid); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index bbf71dcaa682..22afd615646d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -30,6 +30,7 @@ import android.view.View; import android.widget.LinearLayout; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; @@ -99,6 +100,7 @@ protected boolean shouldLockout(long deadline) { protected abstract int getPasswordTextViewId(); protected abstract void resetState(); + protected abstract SecurityMode getSecurityMode(); @Override protected void onFinishInflate() { @@ -209,7 +211,7 @@ private void onPasswordChecked(int userId, boolean matched, int timeoutMs, mCallback.reportUnlockAttempt(userId, true, 0); if (dismissKeyguard) { mDismissing = true; - mCallback.dismiss(true, userId); + mCallback.dismiss(true, userId, getSecurityMode()); } } else { if (isValidPassword) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java index 57b3761c294f..b733d1cfc2c4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -87,7 +87,7 @@ public void onTrustGrantedWithFlags(int flags, int userId) { Log.i(TAG, "TrustAgent dismissed Keyguard."); } dismiss(false /* authenticated */, userId, - /* bypassSecondaryLockScreen */ false); + /* bypassSecondaryLockScreen */ false, SecurityMode.Invalid); } else { mViewMediatorCallback.playTrustedSound(); } @@ -193,12 +193,13 @@ public void showErrorMessage(CharSequence message) { * @return True if the keyguard is done. */ public boolean dismiss(int targetUserId) { - return dismiss(false, targetUserId, false); + return dismiss(false, targetUserId, false, getCurrentSecurityMode()); } public boolean handleBackKey() { if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { - mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser()); + mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser(), + getCurrentSecurityMode()); return true; } return false; @@ -210,9 +211,9 @@ protected KeyguardSecurityContainer getSecurityContainer() { @Override public boolean dismiss(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen) { + boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId, - bypassSecondaryLockScreen); + bypassSecondaryLockScreen, expectedSecurityMode); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index a99b1a1aad87..2cdcf9eddaa2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -23,6 +23,7 @@ import android.view.animation.AnimationUtils; import android.widget.LinearLayout; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; import com.android.systemui.Dependency; @@ -218,4 +219,9 @@ private void enableClipping(boolean enable) { public boolean hasOverlappingRendering() { return false; } + + @Override + public SecurityMode getSecurityMode() { + return SecurityMode.PIN; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 97317cf5580f..1a8a26c07d0a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -38,6 +38,7 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; import java.util.List; @@ -387,4 +388,9 @@ public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } + + @Override + public SecurityMode getSecurityMode() { + return SecurityMode.Password; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index d0e8b040f251..921e30ddc249 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -40,6 +40,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockscreenCredential; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.settingslib.animation.AppearAnimationCreator; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; @@ -363,7 +364,7 @@ private void onPatternChecked(int userId, boolean matched, int timeoutMs, mCallback.reportUnlockAttempt(userId, true, 0); if (dismissKeyguard) { mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); - mCallback.dismiss(true, userId); + mCallback.dismiss(true, userId, SecurityMode.Pattern); } } else { mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java index e38472745234..bc72f7979a74 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -15,14 +15,17 @@ */ package com.android.keyguard; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; + public interface KeyguardSecurityCallback { /** * Dismiss the given security screen. * @param securityVerified true if the user correctly entered credentials for the given screen. * @param targetUserId a user that needs to be the foreground user at the dismissal completion. + * @param expectedSecurityMode The security mode that is invoking this dismiss. */ - void dismiss(boolean securityVerified, int targetUserId); + void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode); /** * Dismiss the given security screen. @@ -30,8 +33,10 @@ public interface KeyguardSecurityCallback { * @param targetUserId a user that needs to be the foreground user at the dismissal completion. * @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen, * if any, during this dismissal. + * @param expectedSecurityMode The security mode that is invoking this dismiss. */ - void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen); + void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen, + SecurityMode expectedSecurityMode); /** * Manually report user activity to keep the device awake. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index bace9324ac93..b2020d9fe2d5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -193,7 +193,7 @@ public void onEnd(WindowInsetsAnimation animation) { // Used to notify the container when something interesting happens. public interface SecurityCallback { public boolean dismiss(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen); + boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode); public void userActivity(); public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); @@ -689,11 +689,20 @@ void showPrimarySecurityScreen(boolean turningOff) { * completion. * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary * secondary lock screen requirement, if any. + * @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid + * indicates that no check should be done * @return true if keyguard is done */ boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen) { + boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); + if (expectedSecurityMode != SecurityMode.Invalid + && expectedSecurityMode != getCurrentSecurityMode()) { + Log.w(TAG, "Attempted to invoke showNextSecurityScreenOrFinish with securityMode " + + expectedSecurityMode + ", but current mode is " + getCurrentSecurityMode()); + return false; + } + boolean finish = false; boolean strongAuth = false; int eventSubtype = -1; @@ -832,14 +841,17 @@ public void onUserInput() { } @Override - public void dismiss(boolean authenticated, int targetId) { - dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); + public void dismiss(boolean authenticated, int targetId, + SecurityMode expectedSecurityMode) { + dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false, + expectedSecurityMode); } @Override public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen) { - mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); + boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { + mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen, + expectedSecurityMode); } public boolean isVerifyUnlockOnly() { @@ -891,10 +903,11 @@ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } @Override public boolean isVerifyUnlockOnly() { return false; } @Override - public void dismiss(boolean securityVerified, int targetUserId) { } + public void dismiss(boolean securityVerified, int targetUserId, + SecurityMode expectedSecurityMode) { } @Override public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen) { } + boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { } @Override public void onUserInput() { } @Override @@ -946,8 +959,9 @@ public SecurityMode getCurrentSecuritySelection() { return mCurrentSecuritySelection; } - public void dismiss(boolean authenticated, int targetUserId) { - mCallback.dismiss(authenticated, targetUserId); + public void dismiss(boolean authenticated, int targetUserId, + SecurityMode expectedSecurityMode) { + mCallback.dismiss(authenticated, targetUserId, expectedSecurityMode); } public boolean needsInput() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index 76adf04b21e7..6be80c49bd06 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -37,6 +37,7 @@ import android.view.WindowManager; import android.widget.ImageView; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -348,7 +349,8 @@ public void run() { mRemainingAttempts = -1; mShowDefaultMessage = true; if (mCallback != null) { - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser(), + SecurityMode.SimPin); } } else { mShowDefaultMessage = false; @@ -398,5 +400,10 @@ public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock); } + + @Override + public SecurityMode getSecurityMode() { + return SecurityMode.SimPin; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index a84664ceee3d..0e9b301f03d0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -36,6 +36,7 @@ import android.view.WindowManager; import android.widget.ImageView; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -76,7 +77,8 @@ public void onSimStateChanged(int subId, int slotId, int simState) { // mCallback can be null if onSimStateChanged callback is called when keyguard // isn't active. if (mCallback != null) { - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser(), + SecurityMode.SimPuk); } break; } @@ -422,8 +424,8 @@ public void run() { mRemainingAttempts = -1; mShowDefaultMessage = true; if (mCallback != null) { - mCallback.dismiss(true, - KeyguardUpdateMonitor.getCurrentUser()); + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser(), + SecurityMode.SimPuk); } } else { mShowDefaultMessage = false; @@ -479,6 +481,11 @@ public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_sim_puk_unlock); } + + @Override + public SecurityMode getSecurityMode() { + return SecurityMode.SimPuk; + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index 9be2d124026c..3114b0a99e97 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -46,6 +46,7 @@ import androidx.test.filters.SmallTest; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.SysuiTestCase; import org.junit.After; @@ -191,7 +192,7 @@ private SurfaceView verifySurfaceReady() throws Exception { private void verifyViewDismissed(SurfaceView v) throws Exception { verify(mParent).removeView(v); - verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true); + verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true, SecurityMode.Invalid); assertThat(mContext.isBound(mComponentName)).isFalse(); } } From 7d9e3b24b1f5b4f9ff8aad4e87fe49db1e19a396 Mon Sep 17 00:00:00 2001 From: Chris Ye Date: Sun, 10 May 2020 15:07:31 -0700 Subject: [PATCH 082/208] Change InputWindowInfo::isTrustedOverlay() to be permission and flag based Add private flag to WindowManager.LayoutParams. If the flag is set, check if caller has INTERNAL_SYSTEM_WINDOW permission. Bug: 155781676 Bug: 196389741 Test: atest WindowManagerServiceTests Change-Id: I70151697cc01e8427129f951f0ebadc4805b2d56 Merged-In: I70151697cc01e8427129f951f0ebadc4805b2d56 (cherry picked from commit 716333761d4298432a3d8822df9161bba14da620) Merged-In: I70151697cc01e8427129f951f0ebadc4805b2d56 --- core/java/android/view/IWindowSession.aidl | 5 ++-- core/java/android/view/InputWindowHandle.java | 3 +++ core/java/android/view/WindowManager.java | 27 +++++++++++++------ .../android/view/WindowlessWindowManager.java | 12 +++++---- ...droid_hardware_input_InputWindowHandle.cpp | 4 +++ .../systemui/bubbles/BubbleController.java | 3 ++- .../server/policy/WindowManagerPolicy.java | 2 -- .../com/android/server/wm/DisplayPolicy.java | 12 ++++----- .../java/com/android/server/wm/Session.java | 10 ++++--- .../server/wm/WindowManagerService.java | 15 +++++++---- .../com/android/server/wm/WindowState.java | 21 +++++++++++++++ 11 files changed, 81 insertions(+), 33 deletions(-) diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 0410c9024dcb..819e89b67b38 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -336,11 +336,12 @@ interface IWindowSession { * an input channel where the client can receive input. */ void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window, - in IBinder hostInputToken, int flags, int type, out InputChannel outInputChannel); + in IBinder hostInputToken, int flags, int privateFlags, int type, + out InputChannel outInputChannel); /** * Update the flags on an input channel associated with a particular surface. */ void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface, - int flags, in Region region); + int flags, int privateFlags, in Region region); } diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 71d26b8880f7..b3cf78c5157f 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -82,6 +82,9 @@ public final class InputWindowHandle { // Input event dispatching is paused. public boolean paused; + // Window is trusted overlay. + public boolean trustedOverlay; + // Id of process and user that owns the window. public int ownerPid; public int ownerUid; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 98155de99b6f..eca0045ad436 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1216,14 +1216,6 @@ public static class LayoutParams extends ViewGroup.LayoutParams implements Parce */ public static final int TYPE_STATUS_BAR_ADDITIONAL = FIRST_SYSTEM_WINDOW + 41; - /** - * Similar to TYPE_APPLICATION_OVERLAY, but trusted to overlay other windows since it is - * is coming from the system. - * @hide - */ - // TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready. - public static final int TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42; - /** * End of types of system windows. */ @@ -2044,6 +2036,11 @@ public static boolean isSystemAlertWindowType(@WindowType int type) { */ public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000; + /** + * Flag to indicate that the window is a trusted overlay. + * @hide + */ + public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 0x20000000; /** * An internal annotation for flags that can be specified to {@link #softInputMode}. * @@ -2952,6 +2949,20 @@ public void setFitInsetsIgnoringVisibility(boolean ignore) { privateFlags |= PRIVATE_FLAG_FIT_INSETS_CONTROLLED; } + /** + * Specifies that the window should be considered a trusted system overlay. Trusted system + * overlays are ignored when considering whether windows are obscured during input + * dispatch. Requires the {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} + * permission. + * + * {@see android.view.MotionEvent#FLAG_WINDOW_IS_OBSCURED} + * {@see android.view.MotionEvent#FLAG_WINDOW_IS_PARTIALLY_OBSCURED} + * @hide + */ + public void setTrustedOverlay() { + privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + } + /** * @return the insets types that this window is avoiding overlapping. */ diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index f0006d988163..13f26f5a7fb6 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -115,7 +115,8 @@ protected void setTouchRegion(IBinder window, @Nullable Region region) { if (state.mInputChannelToken != null) { try { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, - state.mSurfaceControl, state.mParams.flags, state.mInputRegion); + state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags, + state.mInputRegion); } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } @@ -144,7 +145,7 @@ public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attr WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) { try { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, - attrs.type, outInputChannel); + attrs.privateFlags, attrs.type, outInputChannel); } catch (RemoteException e) { Log.e(TAG, "Failed to grant input to surface: ", e); } @@ -274,7 +275,7 @@ public int relayout(IWindow window, int seq, WindowManager.LayoutParams inAttrs, && state.mInputChannelToken != null) { try { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, - attrs.flags, state.mInputRegion); + attrs.flags, attrs.privateFlags, state.mInputRegion); } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } @@ -445,12 +446,13 @@ public void reportSystemGestureExclusionChanged(android.view.IWindow window, @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, - IBinder hostInputToken, int flags, int type, InputChannel outInputChannel) { + IBinder hostInputToken, int flags, int privateFlags, int type, + InputChannel outInputChannel) { } @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, Region region) { + int flags, int privateFlags, Region region) { } @Override diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 79f62cb19db0..81569e0f7b7a 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -59,6 +59,7 @@ static struct { jfieldID hasFocus; jfieldID hasWallpaper; jfieldID paused; + jfieldID trustedOverlay; jfieldID ownerPid; jfieldID ownerUid; jfieldID inputFeatures; @@ -151,6 +152,7 @@ bool NativeInputWindowHandle::updateInfo() { gInputWindowHandleClassInfo.hasWallpaper); mInfo.paused = env->GetBooleanField(obj, gInputWindowHandleClassInfo.paused); + mInfo.trustedOverlay = env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay); mInfo.ownerPid = env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid); mInfo.ownerUid = env->GetIntField(obj, @@ -329,6 +331,8 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.paused, clazz, "paused", "Z"); + GET_FIELD_ID(gInputWindowHandleClassInfo.trustedOverlay, clazz, "trustedOverlay", "Z"); + GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz, "ownerPid", "I"); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index fb819f04e0e1..e09a05da655f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -758,13 +758,14 @@ private void addToWindowManagerMaybe() { // themselves. ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_TRUSTED_APPLICATION_OVERLAY, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // Start not focusable - we'll become focusable when expanded so the ActivityView // can use the IME. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); + mWmLayoutParams.setTrustedOverlay(); mWmLayoutParams.setFitInsetsTypes(0); mWmLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mWmLayoutParams.token = new Binder(); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index cadbe51c8043..684e2c4d5243 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -56,7 +56,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; -import static android.view.WindowManager.LayoutParams.TYPE_TRUSTED_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; @@ -796,7 +795,6 @@ default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindo // in a higher layer than TYPE_APPLICATION_OVERLAY. return canAddInternalSystemWindow ? 13 : 10; case TYPE_APPLICATION_OVERLAY: - case TYPE_TRUSTED_APPLICATION_OVERLAY: return 12; case TYPE_INPUT_METHOD: // on-screen keyboards and other such input method user interfaces go here. diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 4f828df3d1e0..1a94ba5ddbf8 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -77,6 +77,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; @@ -99,7 +100,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; -import static android.view.WindowManager.LayoutParams.TYPE_TRUSTED_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; @@ -1024,6 +1024,11 @@ int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, "DisplayPolicy"); } + if ((attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { + mContext.enforcePermission( + android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid, + "DisplayPolicy"); + } switch (attrs.type) { case TYPE_STATUS_BAR: @@ -1069,11 +1074,6 @@ int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, "DisplayPolicy"); break; - case TYPE_TRUSTED_APPLICATION_OVERLAY: - mContext.enforcePermission( - android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid, - "DisplayPolicy"); - break; case TYPE_STATUS_BAR_PANEL: return WindowManagerGlobal.ADD_INVALID_TYPE; } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index e2258093dcbe..86cbf3e3afe1 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -662,7 +662,7 @@ boolean hasAlertWindowSurfaces(DisplayContent displayContent) { @Override public void grantInputChannel(int displayId, SurfaceControl surface, - IWindow window, IBinder hostInputToken, int flags, int type, + IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, InputChannel outInputChannel) { if (hostInputToken == null && !mCanAddInternalSystemWindow) { // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to @@ -678,7 +678,8 @@ public void grantInputChannel(int displayId, SurfaceControl surface, final long identity = Binder.clearCallingIdentity(); try { mService.grantInputChannel(mUid, mPid, displayId, surface, window, hostInputToken, - flags, mCanAddInternalSystemWindow ? type : 0, outInputChannel); + flags, mCanAddInternalSystemWindow ? privateFlags : 0, + mCanAddInternalSystemWindow ? type : 0, outInputChannel); } finally { Binder.restoreCallingIdentity(identity); } @@ -686,10 +687,11 @@ public void grantInputChannel(int displayId, SurfaceControl surface, @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, Region region) { + int flags, int privateFlags, Region region) { final long identity = Binder.clearCallingIdentity(); try { - mService.updateInputChannel(channelToken, displayId, surface, flags, region); + mService.updateInputChannel(channelToken, displayId, surface, flags, + mCanAddInternalSystemWindow ? privateFlags : 0, region); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c25fd12266af..c7f9db64d1ed 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -61,6 +61,7 @@ import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -8086,7 +8087,7 @@ private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, i * views. */ void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface, - IWindow window, IBinder hostInputToken, int flags, int type, + IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, InputChannel outInputChannel) { final InputApplicationHandle applicationHandle; final String name; @@ -8102,7 +8103,7 @@ void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceCon } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags, type, null /* region */); + name, applicationHandle, flags, privateFlags, type, null /* region */); clientChannel.transferTo(outInputChannel); clientChannel.dispose(); @@ -8110,7 +8111,8 @@ void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceCon private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, int displayId, SurfaceControl surface, String name, - InputApplicationHandle applicationHandle, int flags, int type, Region region) { + InputApplicationHandle applicationHandle, int flags, int privateFlags, int type, + Region region) { InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.name = name; @@ -8139,6 +8141,9 @@ private void updateInputChannel(IBinder channelToken, int callingUid, int callin h.setTouchableRegionCrop(surface); } + // Check private trusted overlay flag to set trustedOverlay field of input window handle. + h.trustedOverlay = (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0; + SurfaceControl.Transaction t = mTransactionFactory.get(); t.setInputWindowInfo(surface, h); t.apply(); @@ -8152,7 +8157,7 @@ private void updateInputChannel(IBinder channelToken, int callingUid, int callin * is undefined. */ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags, Region region) { + int flags, int privateFlags, Region region) { final InputApplicationHandle applicationHandle; final String name; final EmbeddedWindowController.EmbeddedWindow win; @@ -8167,7 +8172,7 @@ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surf } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags, win.mWindowType, region); + applicationHandle, flags, privateFlags, win.mWindowType, region); } /** Return whether layer tracing is enabled */ diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 522b8d8fcce8..74b5840512d2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -55,11 +55,14 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; @@ -84,6 +87,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; @@ -947,6 +951,23 @@ public boolean isInteractive() { ? mActivityRecord.getInputApplicationHandle(false /* update */) : null, getDisplayId()); + // Check private trusted overlay flag and window type to set trustedOverlay variable of + // input window handle. + mInputWindowHandle.trustedOverlay = + (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 + && mOwnerCanAddInternalSystemWindow; + mInputWindowHandle.trustedOverlay |= + mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY + || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG + || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR + || mAttrs.type == TYPE_NOTIFICATION_SHADE + || mAttrs.type == TYPE_NAVIGATION_BAR + || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL + || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY + || mAttrs.type == TYPE_DOCK_DIVIDER + || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY + || mAttrs.type == TYPE_INPUT_CONSUMER; + // Make sure we initial all fields before adding to parentWindow, to prevent exception // during onDisplayChanged. if (mIsChildWindow) { From d03d1f514fef88d50098b7eb92b7b285c3d42855 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 15 Jul 2021 10:35:46 -0700 Subject: [PATCH 083/208] Add mechanism for a task's windows to be trusted overlays - Exposes a method to set that a certain part of the SF hierarchy is trusted, and sets this state for tasks in PIP. Bug: 191529039 Bug: 196389741 Test: Manual, try using permission dialog while PIP is active Change-Id: I170cb5a7d22ef569eb36de21cc0bcbef60dd385e Merged-In: I170cb5a7d22ef569eb36de21cc0bcbef60dd385e (cherry picked from commit 47fb132ef2c4ca2896e9b82c4f004542139b6ad1) Merged-In: I170cb5a7d22ef569eb36de21cc0bcbef60dd385e --- core/java/android/view/SurfaceControl.java | 13 +++++++++++ core/jni/android_view_SurfaceControl.cpp | 13 ++++++++++- .../com/android/server/wm/ActivityStack.java | 2 +- .../server/wm/RootWindowContainer.java | 22 ++++++++++++++++++- .../core/java/com/android/server/wm/Task.java | 3 +-- .../android/server/wm/StubTransaction.java | 6 +++++ 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 87b2f4b46df7..1a4b303bd267 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -139,6 +139,8 @@ private static native void nativeSetBackgroundBlurRadius(long transactionObj, lo int blurRadius); private static native void nativeSetLayerStack(long transactionObj, long nativeObject, int layerStack); + private static native void nativeSetTrustedOverlay(long transactionObj, long nativeObject, + boolean isTrustedOverlay); private static native boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); @@ -3037,6 +3039,17 @@ public Transaction setFrameRate(@NonNull SurfaceControl sc, return this; } + /** + * Sets the trusted overlay state on this SurfaceControl and it is inherited to all the + * children. The caller must hold the ACCESS_SURFACE_FLINGER permission. + * @hide + */ + public Transaction setTrustedOverlay(SurfaceControl sc, boolean isTrustedOverlay) { + checkPreconditions(sc); + nativeSetTrustedOverlay(mNativeObject, sc.mNativeObject, isTrustedOverlay); + return this; + } + /** * Merge the other transaction into this transaction, clearing the * other transaction as if it had been applied. diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ae36f8a7b30b..fd2ccbe50b8d 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -627,6 +627,14 @@ static void nativeSetShadowRadius(JNIEnv* env, jclass clazz, jlong transactionOb transaction->setShadowRadius(ctrl, shadowRadius); } +static void nativeSetTrustedOverlay(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jboolean isTrustedOverlay) { + auto transaction = reinterpret_cast(transactionObj); + + SurfaceControl* const ctrl = reinterpret_cast(nativeObject); + transaction->setTrustedOverlay(ctrl, isTrustedOverlay); +} + static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jfloat frameRate, jint compatibility) { auto transaction = reinterpret_cast(transactionObj); @@ -1666,7 +1674,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetGlobalShadowSettings }, {"nativeGetHandle", "(J)J", (void*)nativeGetHandle }, - {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint}, + {"nativeSetFixedTransformHint", "(JJI)V", + (void*)nativeSetFixedTransformHint}, + {"nativeSetTrustedOverlay", "(JJZ)V", + (void*)nativeSetTrustedOverlay }, }; int register_android_view_SurfaceControl(JNIEnv* env) diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 071e9a56e5f5..60fdbf9aea1b 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -715,7 +715,7 @@ && isActivityTypeStandardOrUndefined()) { : WINDOWING_MODE_FULLSCREEN; } if (currentMode == WINDOWING_MODE_PINNED) { - mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned(); + mRootWindowContainer.notifyActivityPipModeChanged(this, null); } if (likelyResolvedMode == WINDOWING_MODE_PINNED && taskDisplayArea.getRootPinnedTask() != null) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index e240af134435..7dcfaf342174 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2240,7 +2240,27 @@ void moveActivityToPinnedStack(ActivityRecord r, String reason) { ensureActivitiesVisible(null, 0, false /* preserveWindows */); resumeFocusedStacksTopActivities(); - mService.getTaskChangeNotificationController().notifyActivityPinned(r); + notifyActivityPipModeChanged(r.getTask(), r); + } + + /** + * Notifies when an activity enters or leaves PIP mode. + * + * @param task the task of {@param r} + * @param r indicates the activity currently in PIP, can be null to indicate no activity is + * currently in PIP mode. + */ + void notifyActivityPipModeChanged(@NonNull Task task, @Nullable ActivityRecord r) { + final boolean inPip = r != null; + if (inPip) { + mService.getTaskChangeNotificationController().notifyActivityPinned(r); + } else { + mService.getTaskChangeNotificationController().notifyActivityUnpinned(); + } + mWindowManager.mPolicy.setPipVisibilityLw(inPip); + mWmService.mTransactionFactory.get() + .setTrustedOverlay(task.getSurfaceControl(), inPip) + .apply(); } void executeAppTransitionForAllDisplay() { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0d1a3436d4cc..228b5054c13f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1152,7 +1152,7 @@ void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer ol && (newParent == null || !newParent.inPinnedWindowingMode())) { // Notify if a task from the pinned stack is being removed // (or moved depending on the mode). - mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned(); + mRootWindowContainer.notifyActivityPipModeChanged(this, null); } } @@ -4649,5 +4649,4 @@ protected boolean isForceHidden() { long getProtoFieldId() { return TASK; } - } diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index d7eedd990f04..4b7e4ec5809a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -277,4 +277,10 @@ public SurfaceControl.Transaction setFixedTransformHint(SurfaceControl sc, public SurfaceControl.Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) { return this; } + + @Override + public SurfaceControl.Transaction setTrustedOverlay(SurfaceControl sc, + boolean isTrustedOverlay) { + return this; + } } From b88a2e634e223492a4895c778cae0fa9883902d1 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 4 Oct 2021 21:50:31 +0000 Subject: [PATCH 084/208] DO NOT MERGE: Revert "Map TYPE_TRUSTED_APPLICATION_OVERLAY to system window type for A11y" This reverts commit 4ec7b5c3fbd80d38f1010631dfa84c1476f7c867. Bug: 196389741 Test: Ensure PIP windows are trusted overlays Change-Id: I49477ffd489ecbc969ab9b9310c2344ab03280e8 (cherry picked from commit 0cf0d420e18ebb88668f01595ef6acfe3b568430) Merged-In: I49477ffd489ecbc969ab9b9310c2344ab03280e8 --- .../server/accessibility/AccessibilityWindowManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index d15c60b9501d..468e93a8f683 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -725,8 +725,7 @@ private int getTypeForWindowManagerWindowType(int windowType) { case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: - case WindowManager.LayoutParams.TYPE_SCREENSHOT: - case WindowManager.LayoutParams.TYPE_TRUSTED_APPLICATION_OVERLAY: { + case WindowManager.LayoutParams.TYPE_SCREENSHOT: { return AccessibilityWindowInfo.TYPE_SYSTEM; } From b96861451cab9c516fa0b7009c7391962548d34d Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Wed, 26 Jan 2022 23:25:07 +0000 Subject: [PATCH 085/208] SurfaceControl: Add setDropInputMode api Introduces an API to drop input events on this SurfaceControl. This policy will be inherited by its children. The caller must hold the ACCESS_SURFACE_FLINGER permission. Options include: ALL: SurfaceControl and its children will not receive any input regardless of whether it has a valid input channel. These policies are used to enable features that allow for a less trusted interaction model between apps. See the bug for more details. Note: this backport doesn't include the oclude mode since its not needed for the security fix. Test: atest libgui_test InputDispatcherDropInputFeatureTest Bug: 197296414 Merged-In: Ifcb4133306a43874e74e8fb0f42b60842daf6f25 Change-Id: Ifcb4133306a43874e74e8fb0f42b60842daf6f25 (cherry picked from commit fad4624dd82c6623d34137d051b76603533ddfc0) Merged-In: Ifcb4133306a43874e74e8fb0f42b60842daf6f25 --- Android.bp | 1 + core/java/android/view/SurfaceControl.java | 16 ++++++++++++++-- core/jni/android_view_SurfaceControl.cpp | 11 +++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Android.bp b/Android.bp index fc64efd93234..ad0e6ca2c3d4 100644 --- a/Android.bp +++ b/Android.bp @@ -255,6 +255,7 @@ filegroup { ":framework_native_aidl", ":gatekeeper_aidl", ":gsiservice_aidl", + ":guiconstants_aidl", ":incidentcompanion_aidl", ":installd_aidl", ":keystore_aidl", diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 1a4b303bd267..0dc7f5ad26fb 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -42,6 +42,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; +import android.gui.DropInputMode; import android.hardware.display.DeviceProductInfo; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -49,7 +50,6 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.Trace; import android.util.ArrayMap; import android.util.Log; import android.util.SparseIntArray; @@ -141,7 +141,8 @@ private static native void nativeSetLayerStack(long transactionObj, long nativeO int layerStack); private static native void nativeSetTrustedOverlay(long transactionObj, long nativeObject, boolean isTrustedOverlay); - + private static native void nativeSetDropInputMode( + long transactionObj, long nativeObject, int flags); private static native boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); private static native boolean nativeClearAnimationFrameStats(); @@ -3050,6 +3051,17 @@ public Transaction setTrustedOverlay(SurfaceControl sc, boolean isTrustedOverlay return this; } + /** + * Sets the input event drop mode on this SurfaceControl and its children. The caller must + * hold the ACCESS_SURFACE_FLINGER permission. See {@code InputEventDropMode}. + * @hide + */ + public Transaction setDropInputMode(SurfaceControl sc, @DropInputMode int mode) { + checkPreconditions(sc); + nativeSetDropInputMode(mNativeObject, sc.mNativeObject, mode); + return this; + } + /** * Merge the other transaction into this transaction, clearing the * other transaction as if it had been applied. diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index fd2ccbe50b8d..3c704ffb8de7 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -671,6 +671,13 @@ static void nativeSetFixedTransformHint(JNIEnv* env, jclass clazz, jlong transac transaction->setFixedTransformHint(ctrl, transformHint); } +static void nativeSetDropInputMode(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jint mode) { + auto transaction = reinterpret_cast(transactionObj); + SurfaceControl* const ctrl = reinterpret_cast(nativeObject); + transaction->setDropInputMode(ctrl, static_cast(mode)); +} + static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds(); jlongArray array = env->NewLongArray(displayIds.size()); @@ -1495,6 +1502,7 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { // ---------------------------------------------------------------------------- static const JNINativeMethod sSurfaceControlMethods[] = { + // clang-format off {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J", (void*)nativeCreate }, {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", @@ -1678,6 +1686,9 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetFixedTransformHint}, {"nativeSetTrustedOverlay", "(JJZ)V", (void*)nativeSetTrustedOverlay }, + {"nativeSetDropInputMode", "(JJI)V", + (void*)nativeSetDropInputMode}, + // clang-format on }; int register_android_view_SurfaceControl(JNIEnv* env) From 51ad6b15bfaad7c26dffbb3c7124421a8bbf692d Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Wed, 2 Feb 2022 17:13:21 +0000 Subject: [PATCH 086/208] Drop input for toast and child surfaces Toasts that do not have the trustedOverlay flag should not receive input. These windows should not have any children, so force this hierarchy of windows to drop all input by setting a flag on the toast window state which will apply the DROP_INPUT flag on all windows with an input channel. This is to prevent malicious apps from parenting surfaces with input channels to the toast window. Test: show toast and check if input feature flag DROP_INPUT id set via dumpsys Bug: b/197296414 Change-Id: I316b76b685ca5030fd8aa91283555efcce4d6994 Merged-In: I316b76b685ca5030fd8aa91283555efcce4d6994 (cherry picked from commit 55c1473bf2fedaacf7bb8ac068e6f9b1a625b5e0) Merged-In: I316b76b685ca5030fd8aa91283555efcce4d6994 --- .../java/com/android/server/wm/DisplayPolicy.java | 15 +++++++++++++++ .../android/server/wm/WindowManagerService.java | 1 + 2 files changed, 16 insertions(+) diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 1a94ba5ddbf8..179777a1cf34 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -149,6 +149,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; +import android.gui.DropInputMode; import android.hardware.input.InputManager; import android.hardware.power.V1_0.PowerHint; import android.os.Handler; @@ -993,6 +994,20 @@ public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams att } } + /** + * Add additional policy if needed to ensure the window or its children should not receive any + * input. + */ + public void setDropInputModePolicy(WindowState win, LayoutParams attrs) { + if (attrs.type == TYPE_TOAST + && (attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) == 0) { + // Toasts should not receive input. These windows should not have any children, so + // force this hierarchy of windows to drop all input. + mService.mTransactionFactory.get() + .setDropInputMode(win.getSurfaceControl(), DropInputMode.ALL).apply(); + } + } + /** * @return {@code true} if the calling activity initiate toast and is visible with * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c7f9db64d1ed..565dc710b6bf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1656,6 +1656,7 @@ public int addWindow(Session session, IWindow client, int seq, win.mToken.addWindow(win); displayPolicy.addWindowLw(win, attrs); + displayPolicy.setDropInputModePolicy(win, win.mAttrs); if (type == TYPE_INPUT_METHOD) { displayContent.setInputMethodWindowLocked(win); imMayMove = false; From 9bf7153fcbfc19de358cbf1d3a4b2743b5806772 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Tue, 19 Jul 2022 10:46:00 +0000 Subject: [PATCH 087/208] Revert "Prevent non-admin users from deleting system apps." This reverts commit 6c870e157994519094e9e50ddf93e57a26779e22. Reason for revert: Regression, DELETE_SYSTEM_APP flag no longer works Change-Id: Id3eb9e08a5404e88c39235d0d47337ed41bc6139 Merged-In: I4e959e296cca9bbdfc8fccc5e5e0e654ca524165 (cherry picked from commit d9089fbe06e77f5ea1773f5d69b641a81e0b5832) Merged-In: Id3eb9e08a5404e88c39235d0d47337ed41bc6139 From 6778ac255d58888378e7cc9c839926c64639e693 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 19 Aug 2022 09:54:23 -0400 Subject: [PATCH 088/208] Limit the size of NotificationChannel and NotificationChannelGroup Test: android.app.NotificationChannelGroupTest Test: android.app.NotificationChannelTest Test: cts NotificationChannelTest Test: cts NotificationChannelGroupTest Bug: 241764350 Bug: 241764340 Bug: 241764135 Bug: 242702935 Bug: 242703118 Bug: 242703202 Bug: 242702851 Bug: 242703217 Bug: 242703556 Change-Id: I0925583ab54d6c81c415859618f6b907ab7baada (cherry picked from commit 3850857cb0e7f26702d5bd601731d7290390fa3b) (cherry picked from commit 6d417539d705bc757ac4923bc4da878af327c812) Merged-In: I0925583ab54d6c81c415859618f6b907ab7baada --- .../java/android/app/NotificationChannel.java | 23 ++-- .../android/app/NotificationChannelGroup.java | 10 +- .../app/NotificationChannelGroupTest.java | 73 ++++++++++++ .../android/app/NotificationChannelTest.java | 106 ++++++++++++++++++ 4 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 core/tests/coretests/src/android/app/NotificationChannelGroupTest.java create mode 100644 core/tests/coretests/src/android/app/NotificationChannelTest.java diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index cf2f7690bc2c..cd46b39574d2 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -78,8 +78,13 @@ public final class NotificationChannel implements Parcelable { /** * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this * limit. + * @hide */ - private static final int MAX_TEXT_LENGTH = 1000; + public static final int MAX_TEXT_LENGTH = 1000; + /** + * @hide + */ + public static final int MAX_VIBRATION_LENGTH = 1000; private static final String TAG_CHANNEL = "channel"; private static final String ATT_NAME = "name"; @@ -236,17 +241,17 @@ public NotificationChannel(String id, CharSequence name, @Importance int importa */ protected NotificationChannel(Parcel in) { if (in.readByte() != 0) { - mId = in.readString(); + mId = getTrimmedString(in.readString()); } else { mId = null; } if (in.readByte() != 0) { - mName = in.readString(); + mName = getTrimmedString(in.readString()); } else { mName = null; } if (in.readByte() != 0) { - mDesc = in.readString(); + mDesc = getTrimmedString(in.readString()); } else { mDesc = null; } @@ -255,18 +260,22 @@ protected NotificationChannel(Parcel in) { mLockscreenVisibility = in.readInt(); if (in.readByte() != 0) { mSound = Uri.CREATOR.createFromParcel(in); + mSound = Uri.parse(getTrimmedString(mSound.toString())); } else { mSound = null; } mLights = in.readByte() != 0; mVibration = in.createLongArray(); + if (mVibration != null && mVibration.length > MAX_VIBRATION_LENGTH) { + mVibration = Arrays.copyOf(mVibration, MAX_VIBRATION_LENGTH); + } mUserLockedFields = in.readInt(); mFgServiceShown = in.readByte() != 0; mVibrationEnabled = in.readByte() != 0; mShowBadge = in.readByte() != 0; mDeleted = in.readByte() != 0; if (in.readByte() != 0) { - mGroup = in.readString(); + mGroup = getTrimmedString(in.readString()); } else { mGroup = null; } @@ -276,8 +285,8 @@ protected NotificationChannel(Parcel in) { mAllowBubbles = in.readInt(); mImportanceLockedByOEM = in.readBoolean(); mOriginalImportance = in.readInt(); - mParentId = in.readString(); - mConversationId = in.readString(); + mParentId = getTrimmedString(in.readString()); + mConversationId = getTrimmedString(in.readString()); mDemoted = in.readBoolean(); mImportantConvo = in.readBoolean(); } diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index 403fb3e3727c..526c0491f03f 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -42,8 +42,9 @@ public final class NotificationChannelGroup implements Parcelable { /** * The maximum length for text fields in a NotificationChannelGroup. Fields will be truncated at * this limit. + * @hide */ - private static final int MAX_TEXT_LENGTH = 1000; + public static final int MAX_TEXT_LENGTH = 1000; private static final String TAG_GROUP = "channelGroup"; private static final String ATT_NAME = "name"; @@ -89,13 +90,14 @@ public NotificationChannelGroup(String id, CharSequence name) { */ protected NotificationChannelGroup(Parcel in) { if (in.readByte() != 0) { - mId = in.readString(); + mId = getTrimmedString(in.readString()); } else { mId = null; } mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mName = getTrimmedString(mName.toString()); if (in.readByte() != 0) { - mDescription = in.readString(); + mDescription = getTrimmedString(in.readString()); } else { mDescription = null; } @@ -119,7 +121,7 @@ public void writeToParcel(Parcel dest, int flags) { } else { dest.writeByte((byte) 0); } - TextUtils.writeToParcel(mName, dest, flags); + TextUtils.writeToParcel(mName.toString(), dest, flags); if (mDescription != null) { dest.writeByte((byte) 1); dest.writeString(mDescription); diff --git a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java new file mode 100644 index 000000000000..2a3da05eabb3 --- /dev/null +++ b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static junit.framework.TestCase.assertEquals; + +import android.os.Parcel; +import android.test.AndroidTestCase; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.common.base.Strings; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NotificationChannelGroupTest { + private final String CLASS = "android.app.NotificationChannelGroup"; + + @Test + public void testLongStringFields() { + NotificationChannelGroup group = new NotificationChannelGroup("my_group_01", "groupName"); + + try { + String longString = Strings.repeat("A", 65536); + Field mName = Class.forName(CLASS).getDeclaredField("mName"); + mName.setAccessible(true); + mName.set(group, longString); + Field mId = Class.forName(CLASS).getDeclaredField("mId"); + mId.setAccessible(true); + mId.set(group, longString); + Field mDescription = Class.forName(CLASS).getDeclaredField("mDescription"); + mDescription.setAccessible(true); + mDescription.set(group, longString); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + Parcel parcel = Parcel.obtain(); + group.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + NotificationChannelGroup fromParcel = + NotificationChannelGroup.CREATOR.createFromParcel(parcel); + assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH, fromParcel.getId().length()); + assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH, fromParcel.getName().length()); + assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH, + fromParcel.getDescription().length()); + } +} diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java new file mode 100644 index 000000000000..647bfe84231d --- /dev/null +++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static junit.framework.TestCase.assertEquals; + +import android.net.Uri; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.common.base.Strings; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NotificationChannelTest { + private final String CLASS = "android.app.NotificationChannel"; + + @Test + public void testLongStringFields() { + NotificationChannel channel = new NotificationChannel("id", "name", 3); + + try { + String longString = Strings.repeat("A", 65536); + Field mName = Class.forName(CLASS).getDeclaredField("mName"); + mName.setAccessible(true); + mName.set(channel, longString); + Field mId = Class.forName(CLASS).getDeclaredField("mId"); + mId.setAccessible(true); + mId.set(channel, longString); + Field mDesc = Class.forName(CLASS).getDeclaredField("mDesc"); + mDesc.setAccessible(true); + mDesc.set(channel, longString); + Field mParentId = Class.forName(CLASS).getDeclaredField("mParentId"); + mParentId.setAccessible(true); + mParentId.set(channel, longString); + Field mGroup = Class.forName(CLASS).getDeclaredField("mGroup"); + mGroup.setAccessible(true); + mGroup.set(channel, longString); + Field mConversationId = Class.forName(CLASS).getDeclaredField("mConversationId"); + mConversationId.setAccessible(true); + mConversationId.set(channel, longString); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + Parcel parcel = Parcel.obtain(); + channel.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + NotificationChannel fromParcel = NotificationChannel.CREATOR.createFromParcel(parcel); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, fromParcel.getId().length()); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, fromParcel.getName().length()); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, + fromParcel.getDescription().length()); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, + fromParcel.getParentChannelId().length()); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, + fromParcel.getGroup().length()); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, + fromParcel.getConversationId().length()); + } + + @Test + public void testLongAlertFields() { + NotificationChannel channel = new NotificationChannel("id", "name", 3); + + channel.setSound(Uri.parse("content://" + Strings.repeat("A",65536)), + Notification.AUDIO_ATTRIBUTES_DEFAULT); + channel.setVibrationPattern(new long[65550/2]); + + Parcel parcel = Parcel.obtain(); + channel.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + NotificationChannel fromParcel = NotificationChannel.CREATOR.createFromParcel(parcel); + assertEquals(NotificationChannel.MAX_VIBRATION_LENGTH, + fromParcel.getVibrationPattern().length); + assertEquals(NotificationChannel.MAX_TEXT_LENGTH, + fromParcel.getSound().toString().length()); + } +} From fcd7769c4f6906ee1a4ff928a81069a143e29a09 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Tue, 9 Aug 2022 17:48:15 +0100 Subject: [PATCH 089/208] Prevent non-admin users from deleting system apps. This addresses a security issue where the guest user can remove updates for system apps. With this CL, attempts to uninstall/downgrade system apps will fail if attempted by a non-admin user, unless the DELETE_SYSTEM_APP flag is specified. This is a fixed version of ag/17408864, to address b/236578018. Bug: 170646036 Test: manual, try uninstalling system app update as guest Merged-In: I4e959e296cca9bbdfc8fccc5e5e0e654ca524165 Change-Id: I6ecfef50294c9000a6ce539bdec6f372c872a40b (cherry picked from commit fbfa268d47c7915b7a87d3fef22a5b8f3bbabeb7) Merged-In: I6ecfef50294c9000a6ce539bdec6f372c872a40b --- .../core/java/com/android/server/pm/PackageManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4c5e966f54c0..7fe821fa43e3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18653,7 +18653,8 @@ int deletePackageX(String packageName, long versionCode, int userId, int deleteF return PackageManager.DELETE_FAILED_INTERNAL_ERROR; } - if (isSystemApp(uninstalledPs)) { + if (isSystemApp(uninstalledPs) + && (deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0) { UserInfo userInfo = mUserManager.getUserInfo(userId); if (userInfo == null || !userInfo.isAdmin()) { Slog.w(TAG, "Not removing package " + packageName From dfcfbbf1ebaa7b517981a389c8507762227f7295 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Thu, 1 Sep 2022 10:14:24 -0700 Subject: [PATCH 090/208] Include all enabled services when FEEDBACK_ALL_MASK. Bug: 243849844 Test: m sts; sts-tradefed run sts-dynamic-develop -m CtsAccessibilityTestCases Change-Id: I4f93e06d1066085bd64e8f09882de2f4a72a0633 (cherry picked from commit 2bc4d49c2b0265f5de1c62d1342b1426cc5e1377) Merged-In: I4f93e06d1066085bd64e8f09882de2f4a72a0633 --- .../server/accessibility/AccessibilityManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 75fcc4c490ea..4d4a98606db0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -791,7 +791,8 @@ public List getEnabledAccessibilityServiceList(int fee final List result = new ArrayList<>(serviceCount); for (int i = 0; i < serviceCount; ++i) { final AccessibilityServiceConnection service = services.get(i); - if ((service.mFeedbackType & feedbackType) != 0) { + if ((service.mFeedbackType & feedbackType) != 0 + || feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) { result.add(service.getServiceInfo()); } } From 773e4d07355b761f5979321b3c1e17d47d9f0e38 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Fri, 9 Sep 2022 14:50:31 -0700 Subject: [PATCH 091/208] [pm] forbid deletion of protected packages BUG: 242996180 Test: adb shell pm uninstall --user 0 com.google.android.apps.work.oobconfig Test: Verified with the command above. Before this CL, the package can be deleted. After this CL, the deletion will fail. Change-Id: Iba408e536b340ea5d66ab499442c0c4f828fa36f (cherry picked from commit 15f85c7fa97fe9faa540e6ad9e850990f46a5cca) Merged-In: Iba408e536b340ea5d66ab499442c0c4f828fa36f (cherry picked from commit fcdc62081c934d35a55ff7e511590337cb4e277a) Merged-In: Iba408e536b340ea5d66ab499442c0c4f828fa36f --- .../android/server/pm/PackageManagerService.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7fe821fa43e3..d4897a0cc9ad 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18292,6 +18292,20 @@ private void deletePackageVersionedInternal(VersionedPackage versionedPackage, final String packageName = versionedPackage.getPackageName(); final long versionCode = versionedPackage.getLongVersionCode(); + + if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { + mHandler.post(() -> { + try { + Slog.w(TAG, "Attempted to delete protected package: " + packageName); + observer.onPackageDeleted(packageName, + PackageManager.DELETE_FAILED_INTERNAL_ERROR, null); + } catch (RemoteException re) { + } + }); + return; + } + + final String internalPackageName; try { From 63af641843c72e0a3f463ec708a1160f0a61f647 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 6 Sep 2022 10:19:06 -0400 Subject: [PATCH 092/208] Fix NPE Test: NotificationChannelGroupTest Test: view notification settings for an app that doesn't use groups Fixes: 244574602 Bug: 241764350 Bug: 241764340 Bug: 241764135 Bug: 242702935 Bug: 242703118 Bug: 242703202 Bug: 242702851 Bug: 242703217 Bug: 242703556 Change-Id: I9c681106f6d645e62b0e44903d40aa523fee0e95 (cherry picked from commit 6f02c07176d0fa4d6985c8f2200ccf49a1657d1c) (cherry picked from commit ad30b85784354a21fb915d47d4dc3ef8828d5d3b) Merged-In: I9c681106f6d645e62b0e44903d40aa523fee0e95 --- .../android/app/NotificationChannelGroup.java | 14 +++++++++++--- .../app/NotificationChannelGroupTest.java | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index 526c0491f03f..07802a220015 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -94,8 +94,11 @@ protected NotificationChannelGroup(Parcel in) { } else { mId = null; } - mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mName = getTrimmedString(mName.toString()); + if (in.readByte() != 0) { + mName = getTrimmedString(in.readString()); + } else { + mName = ""; + } if (in.readByte() != 0) { mDescription = getTrimmedString(in.readString()); } else { @@ -121,7 +124,12 @@ public void writeToParcel(Parcel dest, int flags) { } else { dest.writeByte((byte) 0); } - TextUtils.writeToParcel(mName.toString(), dest, flags); + if (mName != null) { + dest.writeByte((byte) 1); + dest.writeString(mName.toString()); + } else { + dest.writeByte((byte) 0); + } if (mDescription != null) { dest.writeByte((byte) 1); dest.writeString(mDescription); diff --git a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java index 2a3da05eabb3..625c66a4c60e 100644 --- a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java +++ b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java @@ -17,9 +17,11 @@ package android.app; import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; import android.os.Parcel; import android.test.AndroidTestCase; +import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -70,4 +72,18 @@ public void testLongStringFields() { assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH, fromParcel.getDescription().length()); } + + @Test + public void testNullableFields() { + NotificationChannelGroup group = new NotificationChannelGroup("my_group_01", null); + + Parcel parcel = Parcel.obtain(); + group.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + NotificationChannelGroup fromParcel = + NotificationChannelGroup.CREATOR.createFromParcel(parcel); + assertEquals(group.getId(), fromParcel.getId()); + assertTrue(TextUtils.isEmpty(fromParcel.getName())); + } } From 6eede71244f2b44ebeba9825f12a98da15ab30cb Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Thu, 14 Jul 2022 11:25:54 -0700 Subject: [PATCH 093/208] Fix a security issue in app widget service. Bug: 234013191 Test: atest RemoteViewsAdapterTest Change-Id: Icd2eccb7a90124aca18a3dd463c3f79e3a595c20 Merged-In: Icd2eccb7a90124aca18a3dd463c3f79e3a595c20 (cherry picked from commit 263d7d0ba8818c471a27938c4e002bae33569f01) (cherry picked from commit 0ee21ef3e652c78c934d257632a4951bd6d38011) Merged-In: Icd2eccb7a90124aca18a3dd463c3f79e3a595c20 --- core/java/android/appwidget/AppWidgetManager.java | 4 +++- .../com/android/server/appwidget/AppWidgetServiceImpl.java | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 009ec522e436..287331aa6d6e 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -1109,7 +1109,9 @@ public void setBindAppWidgetPermission(String packageName, int userId, boolean p * @param intent The intent of the service which will be providing the data to the * RemoteViewsAdapter. * @param connection The callback interface to be notified when a connection is made or lost. - * @param flags Flags used for binding to the service + * @param flags Flags used for binding to the service. Currently only + * {@link Context#BIND_AUTO_CREATE} and + * {@link Context#BIND_FOREGROUND_SERVICE_WHILE_AWAKE} are supported. * * @see Context#getServiceDispatcher(ServiceConnection, Handler, int) * @hide diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index d7a3a32f102a..f989f73b11b1 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -1315,11 +1315,12 @@ public boolean bindRemoteViewsService(String callingPackage, int appWidgetId, In try { // Ask ActivityManager to bind it. Notice that we are binding the service with the // caller app instead of DevicePolicyManagerService. - if(ActivityManager.getService().bindService( + if (ActivityManager.getService().bindService( caller, activtiyToken, intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), - connection, flags, mContext.getOpPackageName(), - widget.provider.getUserId()) != 0) { + connection, flags & (Context.BIND_AUTO_CREATE + | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE), + mContext.getOpPackageName(), widget.provider.getUserId()) != 0) { // Add it to the mapping of RemoteViewsService to appWidgetIds so that we // can determine when we can call back to the RemoteViewsService later to From 7a5f25f05b15e4b154ff34a5daa045bc3617f98e Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Wed, 14 Sep 2022 16:10:04 +0800 Subject: [PATCH 094/208] [RESTRICT AUTOMERGE] Allow activity to be reparent while allowTaskReparenting is applied MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Any malicious application could hijack tasks by android:allowTaskReparenting. This vulnerability can perform UI spoofing or spying on user’s activities. This CL only allows activities to be reparent while android:allowTaskReparenting is applied and the affinity of activity is same with the target task. Bug: 240663194 Test: atest IntentTests Change-Id: I73abb9ec05af95bc14f887ae825a9ada9600f771 (cherry picked from commit 7da08c6bd31584744e91eb6b3914166344ecae33) Merged-In: I73abb9ec05af95bc14f887ae825a9ada9600f771 --- .../core/java/com/android/server/wm/ResetTargetTaskHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index 32de699eaae9..bf206a3a6bff 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -148,15 +148,16 @@ private boolean processActivity(ActivityRecord r, boolean isTargetTask) { return false; } else { - mResultActivities.add(r); if (r.resultTo != null) { // If this activity is sending a reply to a previous activity, we can't do // anything with it now until we reach the start of the reply chain. // NOTE: that we are assuming the result is always to the previous activity, // which is almost always the case but we really shouldn't count on. + mResultActivities.add(r); return false; } else if (mTargetTaskFound && allowTaskReparenting && mTargetTask.affinity != null && mTargetTask.affinity.equals(r.taskAffinity)) { + mResultActivities.add(r); // This activity has an affinity for our task. Either remove it if we are // clearing or move it over to our task. Note that we currently punt on the case // where we are resetting a task that is not at the top but who has activities From 3477648bc7543d755f54f458181657419ceefe40 Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Wed, 14 Sep 2022 18:05:29 +0000 Subject: [PATCH 095/208] [DO NOT MERGE] Update window with FLAG_SECURE when bouncer is showing This will prevent bouncer interactions from showing up in screenrecords or screenshots. Fixes: 215005011 Test: atest NotificationShadeWindowControllerImpl && take screenshot with bouncer up Merged-In: I3f59df865dc2dd13d4b9ac54bb2dacb7b23f0aa1 Change-Id: Icde7849a181ce8e0c1074537f6dde897aedd8bb3 (cherry picked from commit bc2146966bd931b18ef0a0243508a49be70e7d0b) Merged-In: Icde7849a181ce8e0c1074537f6dde897aedd8bb3 --- .../NotificationShadeWindowController.java | 11 +++++++++ ...NotificationShadeWindowControllerTest.java | 24 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java index 806854dbba0d..523e490eb9c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java @@ -31,6 +31,7 @@ import android.graphics.PixelFormat; import android.os.Binder; import android.os.Handler; +import android.os.Build; import android.os.RemoteException; import android.os.SystemProperties; import android.os.Trace; @@ -280,6 +281,16 @@ private void applyKeyguardFlags(State state) { } Trace.setCounter("display_mode_id", mLpChanged.preferredDisplayModeId); } + + if (state.mBouncerShowing && !isDebuggable()) { + mLpChanged.flags |= LayoutParams.FLAG_SECURE; + } else { + mLpChanged.flags &= ~LayoutParams.FLAG_SECURE; + } + } + + protected boolean isDebuggable() { + return Build.IS_DEBUGGABLE; } private void adjustScreenOrientation(State state) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java index 8c37cf1514fd..6bdc13fa7d59 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java @@ -18,6 +18,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static com.google.common.truth.Truth.assertThat; @@ -83,7 +84,12 @@ public void setUp() { mNotificationShadeWindowController = new NotificationShadeWindowController(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, - mColorExtractor, mDumpManager); + mColorExtractor, mDumpManager) { + @Override + protected boolean isDebuggable() { + return false; + } + }; mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); mNotificationShadeWindowController.attach(); @@ -183,4 +189,20 @@ public void setKeyguardShowing_notFocusable_byDefault() { assertThat((mLayoutParameters.getValue().flags & FLAG_NOT_FOCUSABLE) != 0).isTrue(); assertThat((mLayoutParameters.getValue().flags & FLAG_ALT_FOCUSABLE_IM) == 0).isTrue(); } + + @Test + public void setKeyguardShowing_enablesSecureFlag() { + mNotificationShadeWindowController.setBouncerShowing(true); + + verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); + assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) != 0).isTrue(); + } + + @Test + public void setKeyguardNotShowing_disablesSecureFlag() { + mNotificationShadeWindowController.setBouncerShowing(false); + + verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); + assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue(); + } } From 0df01d7ce2f948ab9583b08f590fa78ac63cc393 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 21 Sep 2022 23:06:31 +0000 Subject: [PATCH 096/208] [Do Not Merge] Ignore malformed shortcuts After an app publishes a shortcut that contains malformed intent, the system can be stuck in boot-loop due to uncaught exception caused by parsing the malformed intent. This CL ignores that particular malformed entry. Since shortcuts are constantly writes back into the xml from system memory, the malformed entry will be removed from the xml the next time system persists shortcuts from memory to file system. Bug: 246540168 Change-Id: I97d5979fc22ad50d0b36db9cc85099b2f28f6109 Test: manual (cherry picked from commit a8fe41afb0a89979386ffd17213eb7b5f1c3739d) Merged-In: I97d5979fc22ad50d0b36db9cc85099b2f28f6109 --- .../com/android/server/pm/ShortcutPackage.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 9e27f65105eb..f2bfb2aa1405 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1798,11 +1798,15 @@ public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortc continue; case TAG_SHORTCUT: - final ShortcutInfo si = parseShortcut(parser, packageName, - shortcutUser.getUserId(), fromBackup); - - // Don't use addShortcut(), we don't need to save the icon. - ret.mShortcuts.put(si.getId(), si); + try { + final ShortcutInfo si = parseShortcut(parser, packageName, + shortcutUser.getUserId(), fromBackup); + // Don't use addShortcut(), we don't need to save the icon. + ret.mShortcuts.put(si.getId(), si); + } catch (Exception e) { + // b/246540168 malformed shortcuts should be ignored + Slog.e(TAG, "Failed parsing shortcut.", e); + } continue; case TAG_SHARE_TARGET: ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser)); From 2c62e7161415e5dd8e6bd1e7f27f5a1bf74fe0f2 Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Mon, 26 Sep 2022 21:35:26 +0800 Subject: [PATCH 097/208] [DO NOT MERGE] Fix permanent denial of service via setComponentEnabledSetting Do not update invalid component enabled settings to prevent the malicious apps from exhausting system server memory. Bug: 240936919 Test: atest android.security.cts.PackageManagerTest Change-Id: I08165337895e89f13a2b9fcce1201cba9ad13d7d (cherry picked from commit 5e98f267592775a2b886ccaa752377d6967f9741) Merged-In: I08165337895e89f13a2b9fcce1201cba9ad13d7d --- .../core/java/com/android/server/pm/PackageManagerService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d4897a0cc9ad..a01451ee8771 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21255,6 +21255,9 @@ && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) } else { Slog.w(TAG, "Failed setComponentEnabledSetting: component class " + className + " does not exist in " + packageName); + // Safetynet logging for b/240936919 + EventLog.writeEvent(0x534e4554, "240936919", callingUid); + return; } } switch (newState) { From 495da93d6a4e3c3ed23783d3ec6a14d155bf3483 Mon Sep 17 00:00:00 2001 From: Hao Ke Date: Tue, 4 Oct 2022 19:43:58 +0000 Subject: [PATCH 098/208] Add safety checks on KEY_INTENT mismatch. For many years, Parcel mismatch typed exploits has been using the AccoungManagerService's passing of KEY_INTENT workflow, as a foothold of launching arbitrary intents. We are adding an extra check on the service side to simulate the final deserialization of the KEY_INTENT value, to make sure the client side won't get a mismatched KEY_INTENT value. Bug: 250588548 Bug: 240138294 Test: atest CtsAccountManagerTestCases Test: local test, also see b/250588548 Change-Id: I433e34f6e21ce15c89825044a15b1dec46bb25cc (cherry picked from commit eb9a0566a583fa13f8aff671c41f78a9e33eab82) Merged-In: I433e34f6e21ce15c89825044a15b1dec46bb25cc --- .../accounts/AccountManagerService.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index e24030188c62..ad41c129e96a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -87,6 +87,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -3022,7 +3023,7 @@ public void onResult(Bundle result) { */ if (!checkKeyIntent( Binder.getCallingUid(), - intent)) { + result)) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "invalid intent in bundle returned"); return; @@ -3433,7 +3434,7 @@ public void onResult(Bundle result) { && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { if (!checkKeyIntent( Binder.getCallingUid(), - intent)) { + result)) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "invalid intent in bundle returned"); return; @@ -4784,7 +4785,13 @@ IAccountManagerResponse getResponseAndClose() { * into launching arbitrary intents on the device via by tricking to click authenticator * supplied entries in the system Settings app. */ - protected boolean checkKeyIntent(int authUid, Intent intent) { + protected boolean checkKeyIntent(int authUid, Bundle bundle) { + if (!checkKeyIntentParceledCorrectly(bundle)) { + EventLog.writeEvent(0x534e4554, "250588548", authUid, ""); + return false; + } + + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); // Explicitly set an empty ClipData to ensure that we don't offer to // promote any Uris contained inside for granting purposes if (intent.getClipData() == null) { @@ -4821,6 +4828,25 @@ protected boolean checkKeyIntent(int authUid, Intent intent) { } } + /** + * Simulate the client side's deserialization of KEY_INTENT value, to make sure they don't + * violate our security policy. + * + * In particular we want to make sure the Authenticator doesn't trick users + * into launching arbitrary intents on the device via exploiting any other Parcel read/write + * mismatch problems. + */ + private boolean checkKeyIntentParceledCorrectly(Bundle bundle) { + Parcel p = Parcel.obtain(); + p.writeBundle(bundle); + p.setDataPosition(0); + Bundle simulateBundle = p.readBundle(); + p.recycle(); + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT); + return (intent.filterEquals(simulateIntent)); + } + private boolean isExportedSystemActivity(ActivityInfo activityInfo) { String className = activityInfo.name; return "android".equals(activityInfo.packageName) && @@ -4967,7 +4993,7 @@ public void onResult(Bundle result) { && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { if (!checkKeyIntent( Binder.getCallingUid(), - intent)) { + result)) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "invalid intent in bundle returned"); return; From b4a40e447ba7361c63822b98ad4ed178ab9ec499 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 19 Aug 2022 17:08:13 +0100 Subject: [PATCH 099/208] Validate package name passed to setApplicationRestrictions. This adds validation that the package name passed to setApplicationRestrictions is in the correct format. This will avoid an issue where a path could be entered resulting in a file being written to an unexpected place. Bug: 239701237 Test: atest UserManagerServiceTest Change-Id: I1ab2b7228470f10ec26fe3a608ae540cfc9e9a96 (cherry picked from commit 31a582490d6e8952d24f267df47d669e3861cf67) Merged-In: I1ab2b7228470f10ec26fe3a608ae540cfc9e9a96 (cherry picked from commit cfcfe6ca8c545f78603c05e23687f8638fd4b51d) (cherry picked from commit 527146512051626bbcbd6e3590c47ceca2e404ab) Merged-In: I1ab2b7228470f10ec26fe3a608ae540cfc9e9a96 --- .../android/server/pm/UserManagerService.java | 41 +++++++++++++++++++ .../server/pm/UserManagerServiceTest.java | 7 ++++ 2 files changed, 48 insertions(+) diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 9576dc567c3f..7307cf24a044 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -88,6 +88,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.EventLog; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; @@ -4123,6 +4124,13 @@ public Bundle getApplicationRestrictionsForUser(String packageName, @UserIdInt i public void setApplicationRestrictions(String packageName, Bundle restrictions, @UserIdInt int userId) { checkSystemOrRoot("set application restrictions"); + String validationResult = validateName(packageName); + if (validationResult != null) { + if (packageName.contains("../")) { + EventLog.writeEvent(0x534e4554, "239701237", -1, ""); + } + throw new IllegalArgumentException("Invalid package name: " + validationResult); + } if (restrictions != null) { restrictions.setDefusable(true); } @@ -4149,6 +4157,39 @@ public void setApplicationRestrictions(String packageName, Bundle restrictions, mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(userId)); } + /** + * Check if the given name is valid. + * + * Note: the logic is taken from FrameworkParsingPackageUtils in master, edited to remove + * unnecessary parts. Copied here for a security fix. + * + * @param name The name to check. + * @return null if it's valid, error message if not + */ + @VisibleForTesting + static String validateName(String name) { + final int n = name.length(); + boolean front = true; + for (int i = 0; i < n; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + if (c == '.') { + front = true; + continue; + } + } + return "bad character '" + c + "'"; + } + return null; + } + private int getUidForPackage(String packageName) { long ident = Binder.clearCallingIdentity(); try { diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java index 6c1c019f504e..658f168b608b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -86,6 +86,13 @@ public void testUserSystemPackageWhitelist() throws Exception { } } + public void testValidateName() { + assertNull(UserManagerService.validateName("android")); + assertNull(UserManagerService.validateName("com.company.myapp")); + assertNotNull(UserManagerService.validateName("/../../data")); + assertNotNull(UserManagerService.validateName("/dir")); + } + private Bundle createBundle() { Bundle result = new Bundle(); // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[] From 42928a5390ca4f2602206f25f30dfbc727cefd8a Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Mon, 7 Nov 2022 19:06:17 +0000 Subject: [PATCH 100/208] [DO NOT MERGE] Revert "Check rule package name in ZenModeHelper.addAutomaticRule" This reverts commit 3201baad70443854e7630a5c301f8bde573a43f7. Reason for revert: broke DND schedules in multi-user mode b/257477671 Change-Id: Ic92180e5fb02796dc0dc2051aa3d3be6e446886e (cherry picked from commit 2643890445797097409a900a295ff8431a41a06a) Merged-In: Ic92180e5fb02796dc0dc2051aa3d3be6e446886e --- .../server/notification/ZenModeHelper.java | 7 ++++- .../notification/ZenModeHelperTest.java | 30 ------------------- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index c96aac6e9914..1c55762f132d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -305,7 +305,7 @@ public AutomaticZenRule getAutomaticZenRule(String id) { public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, String reason) { - if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) { + if (!isSystemRule(automaticZenRule)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { component = getActivityInfo(automaticZenRule.getConfigurationActivity()); @@ -554,6 +554,11 @@ protected void updateDefaultZenRules() { } } + private boolean isSystemRule(AutomaticZenRule rule) { + return rule.getOwner() != null + && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName()); + } + private ServiceInfo getServiceInfo(ComponentName owner) { Intent queryIntent = new Intent(); queryIntent.setComponent(owner); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 88e2c02f63b9..fcf4d88f7c90 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1575,36 +1575,6 @@ public void testAddAutomaticZenRule() { assertEquals(zenRule.getName(), ruleInConfig.name); } - @Test - public void testAddAutomaticZenRule_claimedSystemOwner() { - // Make sure anything that claims to have a "system" owner but not actually part of the - // system package still gets limited on number of rules - for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { - ScheduleInfo si = new ScheduleInfo(); - si.startHour = i; - AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, - new ComponentName("android", "ScheduleConditionProvider" + i), - null, // configuration activity - ZenModeConfig.toScheduleConditionId(si), - new ZenPolicy.Builder().build(), - NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); - assertNotNull(id); - } - try { - AutomaticZenRule zenRule = new AutomaticZenRule("name", - new ComponentName("android", "ScheduleConditionProviderFinal"), - null, // configuration activity - ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), - new ZenPolicy.Builder().build(), - NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); - fail("allowed too many rules to be created"); - } catch (IllegalArgumentException e) { - // yay - } - } - private void setupZenConfig() { mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConfig.allowAlarms = false; From 1ef401685310d924ced578e1e53d0040b1cd251f Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Mon, 7 Nov 2022 19:05:55 +0000 Subject: [PATCH 101/208] [DO NOT MERGE] Revert "Fix system zen rules by using owner package name if caller is system" This reverts commit 0b262d0ae5cd30a8411ca3fafd7919d72b2fa464. Reason for revert: broke DND schedules in multi-user mode b/257477671 Change-Id: I6f89dd0406b6815a94e6b37c029601dfc315b1f8 (cherry picked from commit 8de098ee2f6f75a6e280476378b64ea38fe4f114) Merged-In: I6f89dd0406b6815a94e6b37c029601dfc315b1f8 --- .../NotificationManagerService.java | 11 +----- .../NotificationManagerServiceTest.java | 37 ------------------- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 1cfaf960271f..60d7ec51ca09 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4523,16 +4523,7 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - // If the caller is system, take the package name from the rule's owner rather than - // from the caller's package. - String rulePkg = pkg; - if (isCallingUidSystem()) { - if (automaticZenRule.getOwner() != null) { - rulePkg = automaticZenRule.getOwner().getPackageName(); - } - } - - return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule, + return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, "addAutomaticZenRule"); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index f5cf23125c1b..2239769a3525 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5936,43 +5936,6 @@ public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Except mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } - @Test - public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception { - mService.isSystemUid = true; - ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); - when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) - .thenReturn(true); - mService.setZenHelper(mockZenModeHelper); - ComponentName owner = new ComponentName("android", "ProviderName"); - ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); - boolean isEnabled = true; - AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), - zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule, "com.android.settings"); - - // verify that zen mode helper gets passed in a package name of "android" - verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); - } - - @Test - public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception { - mService.isSystemUid = false; - ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); - when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) - .thenReturn(true); - mService.setZenHelper(mockZenModeHelper); - ComponentName owner = new ComponentName("android", "ProviderName"); - ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); - boolean isEnabled = true; - AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), - zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule, "another.package"); - - // verify that zen mode helper gets passed in the package name from the arg, not the owner - verify(mockZenModeHelper).addAutomaticZenRule( - eq("another.package"), eq(rule), anyString()); - } - @Test public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { try { From e464f83c3e179895dd0bb14726cca6558d066215 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Tue, 13 Sep 2022 12:53:19 -0400 Subject: [PATCH 102/208] Limit lengths of fields in Condition to a max length. This app-generated input needs to not be too long to avoid errors in the process of writing to disk. Bug: 242846316 Test: cts ConditionTest; atest ConditionTest; manually verified exploit apk is OK Change-Id: Ic2fa8f06cc7a4c1f262115764fbd1be2a226b4b9 Merged-In: Ic2fa8f06cc7a4c1f262115764fbd1be2a226b4b9 (cherry picked from commit 81352c3775949c622441e10b468766441e35edc7) (cherry picked from commit 80c0fcf06d5a862c4b05be9896a5d320d2f71fb2) Merged-In: Ic2fa8f06cc7a4c1f262115764fbd1be2a226b4b9 --- .../service/notification/Condition.java | 38 ++++++- .../service/notification/ConditionTest.java | 101 ++++++++++++++++++ 2 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 core/tests/coretests/src/android/service/notification/ConditionTest.java diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index cf57e2590c9c..0057d3e872ed 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -89,6 +89,12 @@ public final class Condition implements Parcelable { public final int flags; public final int icon; + /** + * The maximum string length for any string contained in this condition. + * @hide + */ + public static final int MAX_STRING_LENGTH = 1000; + /** * An object representing the current state of a {@link android.app.AutomaticZenRule}. * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule @@ -103,16 +109,19 @@ public Condition(Uri id, String summary, String line1, String line2, int icon, if (id == null) throw new IllegalArgumentException("id is required"); if (summary == null) throw new IllegalArgumentException("summary is required"); if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state); - this.id = id; - this.summary = summary; - this.line1 = line1; - this.line2 = line2; + this.id = getTrimmedUri(id); + this.summary = getTrimmedString(summary); + this.line1 = getTrimmedString(line1); + this.line2 = getTrimmedString(line2); this.icon = icon; this.state = state; this.flags = flags; } public Condition(Parcel source) { + // This constructor passes all fields directly into the constructor that takes all the + // fields as arguments; that constructor will trim each of the input strings to + // max length if necessary. this((Uri)source.readParcelable(Condition.class.getClassLoader()), source.readString(), source.readString(), @@ -239,4 +248,25 @@ public Condition[] newArray(int size) { return new Condition[size]; } }; + + /** + * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH. + */ + private static String getTrimmedString(String input) { + if (input != null && input.length() > MAX_STRING_LENGTH) { + return input.substring(0, MAX_STRING_LENGTH); + } + return input; + } + + /** + * Returns a truncated copy of the Uri by trimming the string representation to the maximum + * string length. + */ + private static Uri getTrimmedUri(Uri input) { + if (input != null && input.toString().length() > MAX_STRING_LENGTH) { + return Uri.parse(getTrimmedString(input.toString())); + } + return input; + } } diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java new file mode 100644 index 000000000000..42629ba41287 --- /dev/null +++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; + +import android.net.Uri; +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.google.common.base.Strings; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ConditionTest { + private static final String CLASS = "android.service.notification.Condition"; + + @Test + public void testLongFields_inConstructors() { + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + + // Confirm strings are truncated via short constructor + Condition cond1 = new Condition(longUri, longString, Condition.STATE_TRUE); + + assertEquals(Condition.MAX_STRING_LENGTH, cond1.id.toString().length()); + assertEquals(Condition.MAX_STRING_LENGTH, cond1.summary.length()); + + // Confirm strings are truncated via long constructor + Condition cond2 = new Condition(longUri, longString, longString, longString, + -1, Condition.STATE_TRUE, Condition.FLAG_RELEVANT_ALWAYS); + + assertEquals(Condition.MAX_STRING_LENGTH, cond2.id.toString().length()); + assertEquals(Condition.MAX_STRING_LENGTH, cond2.summary.length()); + assertEquals(Condition.MAX_STRING_LENGTH, cond2.line1.length()); + assertEquals(Condition.MAX_STRING_LENGTH, cond2.line2.length()); + } + + @Test + public void testLongFields_viaParcel() { + // Set fields via reflection to force them to be long, then parcel and unparcel to make sure + // it gets truncated upon unparcelling. + Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder", + Condition.STATE_TRUE); + + try { + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + Field id = Class.forName(CLASS).getDeclaredField("id"); + id.setAccessible(true); + id.set(cond, longUri); + Field summary = Class.forName(CLASS).getDeclaredField("summary"); + summary.setAccessible(true); + summary.set(cond, longString); + Field line1 = Class.forName(CLASS).getDeclaredField("line1"); + line1.setAccessible(true); + line1.set(cond, longString); + Field line2 = Class.forName(CLASS).getDeclaredField("line2"); + line2.setAccessible(true); + line2.set(cond, longString); + } catch (NoSuchFieldException e) { + fail(e.toString()); + } catch (ClassNotFoundException e) { + fail(e.toString()); + } catch (IllegalAccessException e) { + fail(e.toString()); + } + + Parcel parcel = Parcel.obtain(); + cond.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Condition fromParcel = new Condition(parcel); + assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.id.toString().length()); + assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.summary.length()); + assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line1.length()); + assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line2.length()); + } +} From 7b6fd9cb5139f1b9d42df0e1f98696a5fe5e7bac Mon Sep 17 00:00:00 2001 From: William Loh Date: Tue, 30 Aug 2022 00:11:15 +0000 Subject: [PATCH 103/208] Limit length and number of MIME types you can set Limit character length of MIME types to 255. If this length is exceeded then a IllegalArugmentException is thrown. The number of MIME types that can be set is also limited to 500 per MIME group with the number of total MIME Groups also limited to 500. A IllegalStateException is thrown if this number is exceeded. Bug: 237291548 Test: Installed and ran POC app from b/237291548 Change-Id: I1d57e674f778cfacdc89225ac3273c432a39af63 Merged-In: I1d57e674f778cfacdc89225ac3273c432a39af63 (cherry picked from commit 3ae3406b9706163073c282a8c4081faa32b606b2) Merged-In: I1d57e674f778cfacdc89225ac3273c432a39af63 --- .../android/content/pm/parsing/ParsingPackageImpl.java | 3 +++ .../core/java/com/android/server/pm/PackageSetting.java | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 295107a06f99..707523a192f0 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -1584,6 +1584,9 @@ private void addMimeGroupsFromComponent(ParsedComponent component) { for (int i = component.getIntents().size() - 1; i >= 0; i--) { IntentFilter filter = component.getIntents().get(i); for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) { + if (mimeGroups != null && mimeGroups.size() > 500) { + throw new IllegalStateException("Max limit on number of MIME Groups reached"); + } mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex)); } } diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 432d7f335ebc..d3f557d18178 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -242,11 +242,20 @@ public boolean isMatch(int flags) { } public boolean setMimeGroup(String mimeGroup, List mimeTypes) { + for (String mimeType : mimeTypes) { + if (mimeType.length() > 255) { + throw new IllegalArgumentException("MIME type length exceeds 255 characters"); + } + } ArraySet oldMimeTypes = getMimeGroupInternal(mimeGroup); if (oldMimeTypes == null) { throw new IllegalArgumentException("Unknown MIME group " + mimeGroup + " for package " + name); } + if (mimeTypes.size() > 500) { + throw new IllegalStateException("Max limit on MIME types for MIME group " + + mimeGroup + " exceeded for package " + name); + } ArraySet newMimeTypes = new ArraySet<>(mimeTypes); boolean hasChanges = !newMimeTypes.equals(oldMimeTypes); From 8a5a68d4bd6232a57d56d1bec40ca20675998ab9 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Wed, 5 Oct 2022 16:28:20 -0700 Subject: [PATCH 104/208] Disable all A11yServices from an uninstalled package. Previous logic would exit the loop after removing the first service matching the uninstalled package. Bug: 243378132 Test: atest AccessibilityEndToEndTest Test: m sts; sts-tradefed run sts-dynamic-develop -m \ CtsAccessibilityServiceTestCases Change-Id: I4ba30345d8600674ee8a9ea3ff411aecbf3655a3 (cherry picked from commit e1f343acdeeddd9a08c9f6c832faf788ce101763) Merged-In: I4ba30345d8600674ee8a9ea3ff411aecbf3655a3 --- .../AccessibilityManagerService.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 4d4a98606db0..03a44131bdd2 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -456,25 +456,27 @@ public void onPackageRemoved(String packageName, int uid) { userState.mBindingServices.removeIf(filter); userState.mCrashedServices.removeIf(filter); final Iterator it = userState.mEnabledServices.iterator(); + boolean anyServiceRemoved = false; while (it.hasNext()) { final ComponentName comp = it.next(); final String compPkg = comp.getPackageName(); if (compPkg.equals(packageName)) { it.remove(); - // Update the enabled services setting. - persistComponentNamesToSettingLocked( - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - userState.mEnabledServices, userId); - // Update the touch exploration granted services setting. userState.mTouchExplorationGrantedServices.remove(comp); - persistComponentNamesToSettingLocked( - Settings.Secure. - TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - userState.mTouchExplorationGrantedServices, userId); - onUserStateChangedLocked(userState); - return; + anyServiceRemoved = true; } } + if (anyServiceRemoved) { + // Update the enabled services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userState.mEnabledServices, userId); + // Update the touch exploration granted services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mTouchExplorationGrantedServices, userId); + onUserStateChangedLocked(userState); + } } } From f607871aaad9030838143e9bff60006c59792235 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Wed, 12 Oct 2022 14:27:46 +0000 Subject: [PATCH 105/208] [DO NOT MERGE] Fix conditionId string trimming in AutomaticZenRule This change only applies to S branches and earlier. Bug: 253085433 Bug: 242703460 Bug: 242703505 Bug: 242703780 Bug: 242704043 Bug: 243794204 Test: AutomaticZenRuleTest Change-Id: Iae423d93b777df8946ecf1c3baf640fcf74990ec Merged-In: Iae423d93b777df8946ecf1c3baf640fcf74990ec (cherry picked from commit 83d23fb275d2bcfb090a9c6efd6c71a5f519372f) Merged-In: Iae423d93b777df8946ecf1c3baf640fcf74990ec --- core/java/android/app/AutomaticZenRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index 37b336382769..9a92515ee794 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -125,7 +125,7 @@ public AutomaticZenRule(Parcel source) { name = getTrimmedString(source.readString()); } interruptionFilter = source.readInt(); - conditionId = source.readParcelable(null); + conditionId = getTrimmedUri(source.readParcelable(null)); owner = getTrimmedComponentName(source.readParcelable(null)); configurationActivity = getTrimmedComponentName(source.readParcelable(null)); creationTime = source.readLong(); From ce52917dfd80ef1c1d36c6433a554e3e7e9fd9d5 Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Thu, 22 Sep 2022 15:23:24 -0700 Subject: [PATCH 106/208] RESTRICT AUTOMERGE Validate permission tree size on permission update Bug: 242537498 Test: manual Change-Id: I15343e84c1802d6b89249106263319a6539fa73b Merged-In: I15343e84c1802d6b89249106263319a6539fa73b (cherry picked from commit 8fdf4530aac096d87c8d6afe64036f972a3835be) Merged-In: I15343e84c1802d6b89249106263319a6539fa73b --- .../android/server/pm/permission/PermissionManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 663f6ebcd73a..e99163c09b1c 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -610,8 +610,8 @@ public boolean addPermission(PermissionInfo info, boolean async) { BasePermission bp = mSettings.getPermissionLocked(info.name); added = bp == null; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); + enforcePermissionCapLocked(info, tree); if (added) { - enforcePermissionCapLocked(info, tree); bp = new BasePermission(info.name, tree.getSourcePackageName(), BasePermission.TYPE_DYNAMIC); } else if (!bp.isDynamic()) { From 681c53fb8f30b67df6df06bc50bfb761a34b5ead Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Wed, 28 Sep 2022 06:46:29 +0000 Subject: [PATCH 107/208] [RESTRICT AUTOMERGE] Trim the activity info of another uid if no privilege The activity info could be from another uid which is different from the app that hosts the task. The information should be trimmed if the caller app doesn't have the privilege. Bug: 243130512 Test: verified locally Test: atest RecentTasksTest Change-Id: Ia343ac70e5bb9aeae718fca6674e1ca491a14512 (cherry picked from commit 401e782b244bf84fd5aab371f60c2e52d6226fb3) (cherry picked from commit 17df433a3d96fb6c00dc7078fd0f8048645ace6a) Merged-In: Ia343ac70e5bb9aeae718fca6674e1ca491a14512 --- .../com/android/server/wm/AppTaskImpl.java | 2 +- .../com/android/server/wm/RecentTasks.java | 8 +++++-- .../com/android/server/wm/RunningTasks.java | 4 ++++ .../core/java/com/android/server/wm/Task.java | 21 +++++++++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index dd1d55b2d54d..2fd5963d653b 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -84,7 +84,7 @@ public ActivityManager.RecentTaskInfo getTaskInfo() { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } return mService.getRecentTasks().createRecentTaskInfo(task, - false /* stripExtras */); + false /* stripExtras */, true /* getTasksAllowed */); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 3fe75a4ab49e..6151b1892fda 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -962,7 +962,7 @@ private ArrayList getRecentTasksImpl(int maxNum, continue; } - res.add(createRecentTaskInfo(task, true /* stripExtras */)); + res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed)); } return res; } @@ -1834,12 +1834,16 @@ void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { /** * Creates a new RecentTaskInfo from a Task. */ - ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) { + ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras, + boolean getTasksAllowed) { ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); tr.fillTaskInfo(rti, stripExtras); // Fill in some deprecated values rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; + if (!getTasksAllowed) { + Task.trimIneffectiveInfo(tr, rti); + } return rti; } diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 3509ba72d058..3c7917bb05d1 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -129,6 +129,10 @@ private RunningTaskInfo createRunningTaskInfo(Task task) { final RunningTaskInfo rti = task.getTaskInfo(); // Fill in some deprecated values rti.id = rti.taskId; + + if (!mAllowed) { + Task.trimIneffectiveInfo(task, rti); + } return rti; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 228b5054c13f..c0e4262863e6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3653,6 +3653,27 @@ void fillTaskInfo(TaskInfo info, boolean stripExtras) { : SCREEN_ORIENTATION_UNSET; } + /** + * Removes the activity info if the activity belongs to a different uid, which is + * different from the app that hosts the task. + */ + static void trimIneffectiveInfo(Task task, TaskInfo info) { + final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing, + false /* traverseTopToBottom */); + final int baseActivityUid = + baseActivity != null ? baseActivity.getUid() : task.effectiveUid; + + if (info.topActivityInfo != null + && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) { + info.topActivity = null; + info.topActivityInfo = null; + } + + if (task.effectiveUid != baseActivityUid) { + info.baseActivity = null; + } + } + /** * Returns a {@link TaskInfo} with information from this task. */ From 9b5d4851e2dec09778c1fb9e673f7a3081da118f Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Fri, 23 Sep 2022 12:04:57 -0700 Subject: [PATCH 108/208] RESTRICT AUTOMERGE Revoke SYSTEM_ALERT_WINDOW on upgrade past api 23 Bug: 221040577 Test: atest PermissionTest23#testPre23AppsWithSystemAlertWindowGetDeniedOnUpgrade Change-Id: I4b4605aaae107875811070dea6d031c5d9f25c96 (cherry picked from commit 14551ab6d2c754d83d6b504549aabb40018d9c6a) Merged-In: I4b4605aaae107875811070dea6d031c5d9f25c96 --- .../server/pm/PackageManagerService.java | 4 +- .../permission/PermissionManagerService.java | 60 +++++++++++++++---- .../PermissionManagerServiceInternal.java | 29 +++------ 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a01451ee8771..9d3cb7012590 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12605,9 +12605,7 @@ private void commitPackageSettings(AndroidPackage pkg, AsyncTask.execute(() -> { if (hasOldPkg) { - mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg, - allPackageNames); - mPermissionManager.revokeStoragePermissionsIfScopeExpanded(pkg, oldPkg); + mPermissionManager.onPackageUpdated(pkg, oldPkg, allPackageNames); } if (hasPermissionDefinitionChanges) { mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged( diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index e99163c09b1c..0a98b8b0df57 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2321,6 +2321,46 @@ private void revokeStoragePermissionsIfScopeExpanded( } + /** + * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and + * then updated past api 23, and the app does not satisfy any of the other SAW permission flags, + * the permission should be revoked. + * + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + */ + private void revokeSystemAlertWindowIfUpgradedPast23( + @NonNull AndroidPackage newPackage, + @NonNull AndroidPackage oldPackage, + @NonNull PermissionCallback permissionCallback) { + if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M + || newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M + || !newPackage.getRequestedPermissions() + .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) { + return; + } + + BasePermission saw; + synchronized (mLock) { + saw = mSettings.getPermissionLocked(Manifest.permission.SYSTEM_ALERT_WINDOW); + } + final PackageSetting ps = (PackageSetting) + mPackageManagerInt.getPackageSetting(newPackage.getPackageName()); + if (grantSignaturePermission(Manifest.permission.SYSTEM_ALERT_WINDOW, newPackage, ps, saw, + ps.getPermissionsState())) { + return; + } + for (int userId : mUserManagerInt.getUserIds()) { + try { + revokePermissionFromPackageForUser(newPackage.getPackageName(), + Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId, permissionCallback); + } catch (IllegalStateException | SecurityException e) { + Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for " + + newPackage.getPackageName() + " user " + userId, e); + } + } + } + /** * We might auto-grant permissions if any permission of the group is already granted. Hence if * the group of a granted permission changes we need to revoke it to avoid having permissions of @@ -4797,24 +4837,20 @@ public boolean isPermissionsReviewRequired(@NonNull AndroidPackage pkg, return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId); } /** - * If the app is updated, and has scoped storage permissions, then it is possible that the - * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. + * If the app is updated, then some checks need to be performed to ensure the + * package is not attempting to expoit permission changes across API boundaries. * @param newPackage The new package that was installed * @param oldPackage The old package that was updated + * @param allPackageNames The current packages in the system */ - public void revokeStoragePermissionsIfScopeExpanded( - @NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage - ) { - PermissionManagerService.this.revokeStoragePermissionsIfScopeExpanded(newPackage, - oldPackage, mDefaultPermissionCallback); - } - - @Override - public void revokeRuntimePermissionsIfGroupChanged( + public void onPackageUpdated( @NonNull AndroidPackage newPackage, @NonNull AndroidPackage oldPackage, @NonNull ArrayList allPackageNames) { + PermissionManagerService.this.revokeStoragePermissionsIfScopeExpanded(newPackage, + oldPackage, mDefaultPermissionCallback); + PermissionManagerService.this.revokeSystemAlertWindowIfUpgradedPast23(newPackage, + oldPackage, mDefaultPermissionCallback); PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage, oldPackage, allPackageNames, mDefaultPermissionCallback); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index df0edfa16924..7003c7a2027e 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -239,16 +239,14 @@ public abstract void updatePermissions(@NonNull String packageName, public abstract void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId); - /** - * We might auto-grant permissions if any permission of the group is already granted. Hence if - * the group of a granted permission changes we need to revoke it to avoid having permissions of - * the new group auto-granted. - * - * @param newPackage The new package that was installed - * @param oldPackage The old package that was updated - * @param allPackageNames All packages - */ - public abstract void revokeRuntimePermissionsIfGroupChanged( + /** + * If the app is updated, then some checks need to be performed to ensure the package is not + * attempting to expoit permission changes across API boundaries. + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + * @param allPackageNames The current packages in the system + */ + public abstract void onPackageUpdated( @NonNull AndroidPackage newPackage, @NonNull AndroidPackage oldPackage, @NonNull ArrayList allPackageNames); @@ -265,17 +263,6 @@ public abstract void revokeRuntimePermissionsIfPermissionDefinitionChanged( @NonNull List permissionsToRevoke, @NonNull ArrayList allPackageNames); - /** - * If the app is updated, and has scoped storage permissions, then it is possible that the - * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. - * @param newPackage The new package that was installed - * @param oldPackage The old package that was updated - */ - public abstract void revokeStoragePermissionsIfScopeExpanded( - @NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage - ); - /** * Add all permissions in the given package. *

From e2afd05b709576014439b7854b97eebb805ba90f Mon Sep 17 00:00:00 2001 From: Khoa Hong Date: Fri, 23 Sep 2022 17:05:39 +0800 Subject: [PATCH 109/208] Add protections against queueing a UsbRequest when the underlying UsbDeviceConnection is closed. Bug: 204584366 Test: CTS Verifier: USB Accessory Test & USB Device Test Test: No HWASan use-after-free reports with a test app Change-Id: Ia3a9b10349efc0236b1539c81465f479cb32e02b (cherry picked from commit 1691b54b1fda4239249a3871d2c17ed1ec753061) Merged-In: Ia3a9b10349efc0236b1539c81465f479cb32e02b --- .../hardware/usb/UsbDeviceConnection.java | 28 ++++++++ .../java/android/hardware/usb/UsbRequest.java | 68 ++++++++++++++++--- 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 53a5785f7c76..23f4c6301ec1 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -107,6 +107,34 @@ boolean isOpen() { } } + /** + * This is meant to be called by UsbRequest's queue() in order to synchronize on + * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. + */ + /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) { + synchronized (mLock) { + if (!isOpen()) { + return false; + } + + return request.queueIfConnectionOpen(buffer, length); + } + } + + /** + * This is meant to be called by UsbRequest's queue() in order to synchronize on + * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. + */ + /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) { + synchronized (mLock) { + if (!isOpen()) { + return false; + } + + return request.queueIfConnectionOpen(buffer); + } + } + /** * Releases all system resources related to the device. * Once the object is closed it cannot be used again. diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index 473df712e3f9..c5573214048c 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -113,11 +113,13 @@ public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) * Releases all resources related to this request. */ public void close() { - if (mNativeContext != 0) { - mEndpoint = null; - mConnection = null; - native_close(); - mCloseGuard.close(); + synchronized (mLock) { + if (mNativeContext != 0) { + mEndpoint = null; + mConnection = null; + native_close(); + mCloseGuard.close(); + } } } @@ -191,10 +193,32 @@ public void setClientData(Object data) { */ @Deprecated public boolean queue(ByteBuffer buffer, int length) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { + // The expected exception by CTS Verifier - USB Device test + throw new NullPointerException("invalid connection"); + } + + // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent + // the connection being closed while queueing. + return connection.queueRequest(this, buffer, length); + } + + /** + * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over + * there, to prevent the connection being closed while queueing. + */ + /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) { + UsbDeviceConnection connection = mConnection; + if (connection == null || !connection.isOpen()) { + // The expected exception by CTS Verifier - USB Device test + throw new NullPointerException("invalid connection"); + } + boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); boolean result; - if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P + if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P && length > MAX_USBFS_BUFFER_SIZE) { length = MAX_USBFS_BUFFER_SIZE; } @@ -243,6 +267,28 @@ public boolean queue(ByteBuffer buffer, int length) { * @return true if the queueing operation succeeded */ public boolean queue(@Nullable ByteBuffer buffer) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { + // The expected exception by CTS Verifier - USB Device test + throw new IllegalStateException("invalid connection"); + } + + // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent + // the connection being closed while queueing. + return connection.queueRequest(this, buffer); + } + + /** + * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over + * there, to prevent the connection being closed while queueing. + */ + /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) { + UsbDeviceConnection connection = mConnection; + if (connection == null || !connection.isOpen()) { + // The expected exception by CTS Verifier - USB Device test + throw new IllegalStateException("invalid connection"); + } + // Request need to be initialized Preconditions.checkState(mNativeContext != 0, "request is not initialized"); @@ -260,7 +306,7 @@ public boolean queue(@Nullable ByteBuffer buffer) { mIsUsingNewQueue = true; wasQueued = native_queue(null, 0, 0); } else { - if (mConnection.getContext().getApplicationInfo().targetSdkVersion + if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) { // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, @@ -363,11 +409,12 @@ public boolean queue(@Nullable ByteBuffer buffer) { * @return true if cancelling succeeded */ public boolean cancel() { - if (mConnection == null) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { return false; } - return mConnection.cancelRequest(this); + return connection.cancelRequest(this); } /** @@ -382,7 +429,8 @@ public boolean cancel() { * @return true if cancelling succeeded. */ /* package */ boolean cancelIfOpen() { - if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) { + UsbDeviceConnection connection = mConnection; + if (mNativeContext == 0 || (connection != null && !connection.isOpen())) { Log.w(TAG, "Detected attempt to cancel a request on a connection which isn't open"); return false; From d1a58f1eaf27fe6bec27b1693558d97e94aba83e Mon Sep 17 00:00:00 2001 From: Liahav Eitan Date: Tue, 11 Oct 2022 13:20:52 +0000 Subject: [PATCH 110/208] Fix sharing to another profile where an app has multiple targets Moves the fixUris call from onTargetSelected directly to the intent launch to ensure the intent which is actually started is updated with userId specific URIs. This is a backport of ag/19657256 and ag/20063949. Bug:242165528 Bug:244876518 Bug:242605257 Test: manually share image from personal profile to work gmail, first with chat target then backing up and selecting the main target Test: manually share image from work Photos app to personal WhatsApp's frequent contact target. Change-Id: Id815984e691bf962e19e30a54f7247d16060b3b8 Merged-In: Id815984e691bf962e19e30a54f7247d16060b3b8 Merged-In: Ib41c8a3c46afcc2d62a4c1a924212bcd98bcfbe4 Merged-In: Iabf5dcf2612fe718f2f0886e2e5e9b76f37af1e1 (cherry picked from commit f50ced5f1e619d7fa7858748d6a9dbe861354f04) Merged-In: Id815984e691bf962e19e30a54f7247d16060b3b8 --- .../com/android/internal/app/ResolverActivity.java | 12 ------------ .../internal/app/chooser/DisplayResolveInfo.java | 2 ++ .../internal/app/chooser/SelectableTargetInfo.java | 1 + .../com/android/internal/app/chooser/TargetInfo.java | 11 +++++++++++ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index c5b84f7c9c83..998000c19cd0 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1233,9 +1233,6 @@ protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { } if (target != null) { - if (intent != null && isLaunchingTargetInOtherProfile()) { - prepareIntentForCrossProfileLaunch(intent); - } safelyStartActivity(target); // Rely on the ActivityManager to pop up a dialog regarding app suspension @@ -1248,15 +1245,6 @@ protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { return true; } - private void prepareIntentForCrossProfileLaunch(Intent intent) { - intent.fixUris(UserHandle.myUserId()); - } - - private boolean isLaunchingTargetInOtherProfile() { - return mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier() - != UserHandle.myUserId(); - } - @VisibleForTesting public void safelyStartActivity(TargetInfo cti) { // We're dispatching intents that might be coming from legacy apps, so diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index fe0e7d012262..cbbfbdd88a6c 100644 --- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -178,6 +178,7 @@ public boolean startAsCaller(ResolverActivity activity, Bundle options, int user if (ENABLE_CHOOSER_DELEGATE) { return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); } else { + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); activity.startActivityAsCaller(mResolvedIntent, options, null, false, userId); return true; } @@ -185,6 +186,7 @@ public boolean startAsCaller(ResolverActivity activity, Bundle options, int user @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); activity.startActivityAsUser(mResolvedIntent, options, user); return false; } diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java index 900e18d468bb..9d057b34363e 100644 --- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java +++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java @@ -230,6 +230,7 @@ public boolean startAsCaller(ResolverActivity activity, Bundle options, int user } intent.setComponent(mChooserTarget.getComponentName()); intent.putExtras(mChooserTarget.getIntentExtras()); + TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId); // Important: we will ignore the target security checks in ActivityManager // if and only if the ChooserTarget's target package is the same package diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java index f56ab17cb059..7bb7ddc65c6d 100644 --- a/core/java/com/android/internal/app/chooser/TargetInfo.java +++ b/core/java/com/android/internal/app/chooser/TargetInfo.java @@ -130,4 +130,15 @@ public interface TargetInfo { * @return true if this target should be pinned to the front by the request of the user */ boolean isPinned(); + + /** + * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called + * before launching the intent as another user. + */ + static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { + final int currentUserId = UserHandle.myUserId(); + if (targetUserId != currentUserId) { + intent.fixUris(currentUserId); + } + } } From f404756c93cb7d436a0f74e377578e2c3fda675d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 18 Oct 2022 05:21:30 +0000 Subject: [PATCH 111/208] Ensure that only SysUI can override pending intent launch flags - Originally added in ag/5139951, this method ensured that activities launched from widgets are always started in a new task (if the activity is launched in the home task, the task is not brough forward with the recents transition). We can restrict this to only recents callers since this only applies to 1p launchers in gesture nav (both the gesture with 3p launchers and button nav in general will always start the home intent directly, which makes adding the NEW_TASK flag unnecessary). Bug: 243794108 Test: Ensure that the original bug b/112508020 still works (with the test app in the bug, swipe up still works after launching an activity from the widget, and fails without applying the override flags) Change-Id: Id53c6a2aa6da5933d488ca06a0bfc4ef89a4c343 (cherry picked from commit c4d3106e347922610f8c554de3ae238175ed393e) Merged-In: Id53c6a2aa6da5933d488ca06a0bfc4ef89a4c343 --- .../com/android/server/am/PendingIntentRecord.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 1997dbd6fc37..818b70d61d89 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -326,11 +326,16 @@ public int sendInner(int code, Intent intent, String resolvedType, IBinder white resolvedType = key.requestResolvedType; } - // Apply any launch flags from the ActivityOptions. This is to ensure that the caller - // can specify a consistent launch mode even if the PendingIntent is immutable + // Apply any launch flags from the ActivityOptions. This is used only by SystemUI + // to ensure that we can launch the pending intent with a consistent launch mode even + // if the provided PendingIntent is immutable (ie. to force an activity to launch into + // a new task, or to launch multiple instances if supported by the app) final ActivityOptions opts = ActivityOptions.fromBundle(options); if (opts != null) { - finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + // TODO(b/254490217): Move this check into SafeActivityOptions + if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) { + finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + } } // Extract options before clearing calling identity From ea419280cd0c0f6e0eea638380d7d7441dfba0fc Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Thu, 28 Apr 2022 16:35:56 +0200 Subject: [PATCH 112/208] Make Activites touch opaque - DO NOT MERGE Block touches from passing through activities by adding a dedicated surface that consumes all touches that would otherwise pass through the bounds availble to the Activity. Bug: 194480991 Test: atest CtsWindowManagerDeviceTestCases:ActivityRecordInputSinkTests Test: atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests Test: atest CtsWindowManagerDeviceTestCases:PinnedStackTests Test: Manually ran through go/wm-smoke (verified pip and splitscreen still work) Test: Manually verfied that freeform windows work and don't consume touches outside their bounds Change-Id: I01b35e34a63868dead4e728a3d359ae0942302f9 (cherry picked from commit 74ce78dfb4179cb317d6e2fc3cabe5f60af5d02d) Merged-In: I01b35e34a63868dead4e728a3d359ae0942302f9 --- .../com/android/server/wm/ActivityRecord.java | 13 ++ .../server/wm/ActivityRecordInputSink.java | 113 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 92dbfa4f1733..e5a069847f5c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -673,6 +673,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; + private final ActivityRecordInputSink mActivityRecordInputSink; + + // Activities with this uid are allowed to not create an input sink while being in the same + // task and directly above this ActivityRecord. This field is updated whenever a new activity + // is launched from this ActivityRecord. Touches are always allowed within the same uid. + int mAllowedTouchUid; + private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1650,6 +1657,8 @@ boolean isResolverOrChildActivity() { ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; mHandoverLaunchDisplayId = options.getLaunchDisplayId(); } + + mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3173,6 +3182,7 @@ private void cleanUpActivityServices() { @Override void removeImmediately() { onRemovedFromDisplay(); + mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6048,6 +6058,9 @@ void prepareSurfaces() { } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } + if (show) { + mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); + } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java new file mode 100644 index 000000000000..95b5cec9a144 --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.os.Process; +import android.view.InputWindowHandle; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * Creates a InputWindowHandle that catches all touches that would otherwise pass through an + * Activity. + */ +class ActivityRecordInputSink { + + private final ActivityRecord mActivityRecord; + private final String mName; + + private InputWindowHandle mInputWindowHandle; + private SurfaceControl mSurfaceControl; + + ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { + mActivityRecord = activityRecord; + mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " + + mActivityRecord.mActivityComponent.flattenToShortString(); + if (sourceRecord != null) { + sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); + } + } + + public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { + boolean windowHandleChanged = updateInputWindowHandle(); + if (mSurfaceControl == null) { + mSurfaceControl = createSurface(transaction); + } + if (windowHandleChanged) { + transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); + } + } + + private SurfaceControl createSurface(SurfaceControl.Transaction t) { + SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) + .setName(mName) + .setHidden(false) + .setCallsite("ActivityRecordInputSink.createSurface") + .build(); + // Put layer below all siblings (and the parent surface too) + t.setLayer(surfaceControl, Integer.MIN_VALUE); + return surfaceControl; + } + + private boolean updateInputWindowHandle() { + boolean changed = false; + if (mInputWindowHandle == null) { + mInputWindowHandle = createInputWindowHandle(); + changed = true; + } + // Don't block touches from passing through to an activity below us in the same task, if + // that activity is either from the same uid or if that activity has launched an activity + // in our uid. + final ActivityRecord activityBelowInTask = + mActivityRecord.getTask().getActivityBelow(mActivityRecord); + final boolean allowPassthrough = activityBelowInTask != null && ( + activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() + || activityBelowInTask.isUid(mActivityRecord.getUid())); + boolean notTouchable = (mInputWindowHandle.layoutParamsFlags + & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; + if (allowPassthrough || mActivityRecord.isAppTransitioning()) { + mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= !notTouchable; + } else { + mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= notTouchable; + } + return changed; + } + + private InputWindowHandle createInputWindowHandle() { + InputWindowHandle inputWindowHandle = new InputWindowHandle(null, + mActivityRecord.getDisplayId()); + inputWindowHandle.replaceTouchableRegionWithCrop = true; + inputWindowHandle.name = mName; + inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; + inputWindowHandle.ownerUid = Process.myUid(); + inputWindowHandle.ownerPid = Process.myPid(); + inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + inputWindowHandle.inputFeatures = + WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; + return inputWindowHandle; + } + + void releaseSurfaceControl() { + if (mSurfaceControl != null) { + mSurfaceControl.release(); + mSurfaceControl = null; + } + } + +} From b631111b7532835059690922762605fddaab8403 Mon Sep 17 00:00:00 2001 From: Jackal Guo Date: Tue, 25 Oct 2022 15:01:33 +0800 Subject: [PATCH 113/208] [RESTRICT AUTOMERGE] Correct the behavior of ACTION_PACKAGE_DATA_CLEARED This action should be only broadcasted when the user data is cleared successfully. Broadcasting this action when failed case may result in unexpected result. Bug: 240267890 Test: manually using the PoC in the buganizer to ensure the symptom no longer exists. Change-Id: I53b67eb0a200f9dfb6fa2559227b7c735f30624f (cherry picked from commit 0587cd294ae958af5ce7dd505fa919b4e3a13a6a) Merged-In: I53b67eb0a200f9dfb6fa2559227b7c735f30624f --- .../server/am/ActivityManagerService.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a8d981e2f64c..0054a8f8d033 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4288,20 +4288,24 @@ public void onRemoveCompleted(String packageName, boolean succeeded) finishForceStopPackageLocked(packageName, appInfo.uid); } } - final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, - Uri.fromParts("package", packageName, null)); - intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1); - intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId); - if (isInstantApp) { - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent, - null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, - false, false, resolvedUserId, false); - } else { - broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent, - null, null, 0, null, null, null, null, false, false, resolvedUserId, - false); + + if (succeeded) { + final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, + Uri.fromParts("package", packageName, null /* fragment */)); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + intent.putExtra(Intent.EXTRA_UID, + (appInfo != null) ? appInfo.uid : Process.INVALID_UID); + intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId); + if (isInstantApp) { + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + } + + broadcastIntentInPackage("android", null /* featureId */, SYSTEM_UID, + uid, pid, intent, null /* resolvedType */, null /* resultTo */, + 0 /* resultCode */, null /* resultData */, null /* resultExtras */, + isInstantApp ? permission.ACCESS_INSTANT_APPS : null, + null /* bOptions */, false /* serialized */, false /* sticky */, + resolvedUserId, false /* allowBackgroundActivityStarts */); } if (observer != null) { From 8c22f850afe26a3443050e716900d243692b017f Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Tue, 22 Nov 2022 22:54:01 +0000 Subject: [PATCH 114/208] Convert argument to intent in ChooseTypeAndAccountActivity Bug: 244154558 Test: manual Change-Id: I5a86639cd571e14e9a9f5d5ded631b5a7c08db7e (cherry picked from commit ede0a767c26f144e38b4a0c1c2f530b05ffd29a8) Merged-In: I5a86639cd571e14e9a9f5d5ded631b5a7c08db7e --- core/java/android/accounts/ChooseTypeAndAccountActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index 8cdc6a71caf8..adfe171f1a8d 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -407,7 +407,7 @@ public void run(final AccountManagerFuture accountManagerFuture) { mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage, mCallingUid); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); - startActivityForResult(intent, REQUEST_ADD_ACCOUNT); + startActivityForResult(new Intent(intent), REQUEST_ADD_ACCOUNT); return; } } catch (OperationCanceledException e) { From 01af156bc66876eafc52b0a71d26904f74ac2d8c Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Wed, 9 Nov 2022 15:26:20 -0500 Subject: [PATCH 115/208] Use rule package name in addAutomaticZenRule; specify "android" for all system apps This is a roll forward of two reverted changes combined into one: commit b6d04416628ab29df57efcd738332912d9260cea commit e5e51116fb767162966a8e0d23fafb4f0ff46e86 It additionally fixes an issue where in multi-user profiles (such as a guest user), rules would be incorrectly identified as not created by the system and would therefore fail to be created in settings. Bug: 257477671 Bug: 245236706 Bug: 242537431 Test: NotificationManagerServiceTest; ZenModeHelperTest; manually verified that it's possible to create zen schedules from guest mode Change-Id: I0c4c705cfe5fc875151958957daaf8657fbc21a7 Merged-In: I0c4c705cfe5fc875151958957daaf8657fbc21a7 (cherry picked from commit 7261cdd30bf18965d421fc28c68c61e380bc952d) (cherry picked from commit 7e58ab94f9e3b2f65ae18d515a81c1a31fd4dde6) Merged-In: I0c4c705cfe5fc875151958957daaf8657fbc21a7 --- .../NotificationManagerService.java | 17 +++- .../server/notification/ZenModeHelper.java | 7 +- .../NotificationManagerServiceTest.java | 66 ++++++++++++++ .../notification/ZenModeHelperTest.java | 90 +++++++++++++++++++ 4 files changed, 173 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 60d7ec51ca09..f6ead70b446d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4523,7 +4523,16 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, + // If the calling app is the system (from any user), take the package name from the + // rule's owner rather than from the caller's package. + String rulePkg = pkg; + if (isCallingAppIdSystem()) { + if (automaticZenRule.getOwner() != null) { + rulePkg = automaticZenRule.getOwner().getPackageName(); + } + } + + return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule, "addAutomaticZenRule"); } @@ -8731,6 +8740,12 @@ protected boolean isCallingUidSystem() { return uid == Process.SYSTEM_UID; } + protected boolean isCallingAppIdSystem() { + final int uid = Binder.getCallingUid(); + final int appid = UserHandle.getAppId(uid); + return appid == Process.SYSTEM_UID; + } + protected boolean isUidSystemOrPhone(int uid) { final int appid = UserHandle.getAppId(uid); return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 1c55762f132d..c96aac6e9914 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -305,7 +305,7 @@ public AutomaticZenRule getAutomaticZenRule(String id) { public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, String reason) { - if (!isSystemRule(automaticZenRule)) { + if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { component = getActivityInfo(automaticZenRule.getConfigurationActivity()); @@ -554,11 +554,6 @@ protected void updateDefaultZenRules() { } } - private boolean isSystemRule(AutomaticZenRule rule) { - return rule.getOwner() != null - && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName()); - } - private ServiceInfo getServiceInfo(ComponentName owner) { Intent queryIntent = new Intent(); queryIntent.setComponent(owner); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 2239769a3525..6a8815faa9e6 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -308,6 +308,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private static class TestableNotificationManagerService extends NotificationManagerService { int countSystemChecks = 0; boolean isSystemUid = true; + boolean isSystemAppId = true; int countLogSmartSuggestionsVisible = 0; // If true, don't enqueue the PostNotificationRunnables, just trap them boolean trapEnqueuedNotifications = false; @@ -334,6 +335,12 @@ protected boolean isCallingUidSystem() { return isSystemUid; } + @Override + protected boolean isCallingAppIdSystem() { + countSystemChecks++; + return isSystemUid || isSystemAppId; + } + @Override protected boolean isCallerSystemOrPhone() { countSystemChecks++; @@ -5936,6 +5943,65 @@ public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Except mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } + @Test + public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception { + mService.isSystemUid = true; + ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mockZenModeHelper); + ComponentName owner = new ComponentName("android", "ProviderName"); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule, "com.android.settings"); + + // verify that zen mode helper gets passed in a package name of "android" + verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); + } + + @Test + public void testAddAutomaticZenRule_systemAppIdCallTakesPackageFromOwner() throws Exception { + // The multi-user case: where the calling uid doesn't match the system uid, but the calling + // *appid* is the system. + mService.isSystemUid = false; + mService.isSystemAppId = true; + ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mockZenModeHelper); + ComponentName owner = new ComponentName("android", "ProviderName"); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule, "com.android.settings"); + + // verify that zen mode helper gets passed in a package name of "android" + verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); + } + + @Test + public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception { + mService.isSystemUid = false; + mService.isSystemAppId = false; + ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mockZenModeHelper); + ComponentName owner = new ComponentName("android", "ProviderName"); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule, "another.package"); + + // verify that zen mode helper gets passed in the package name from the arg, not the owner + verify(mockZenModeHelper).addAutomaticZenRule( + eq("another.package"), eq(rule), anyString()); + } + @Test public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { try { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index fcf4d88f7c90..1a1e3ddd968e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1575,6 +1575,96 @@ public void testAddAutomaticZenRule() { assertEquals(zenRule.getName(), ruleInConfig.name); } + @Test + public void testAddAutomaticZenRule_beyondSystemLimit() { + for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { + ScheduleInfo si = new ScheduleInfo(); + si.startHour = i; + AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, + null, + new ComponentName("android", "ScheduleConditionProvider"), + ZenModeConfig.toScheduleConditionId(si), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + // We need the package name to be something that's not "android" so there aren't any + // existing rules under that package. + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + assertNotNull(id); + } + try { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + null, + new ComponentName("android", "ScheduleConditionProvider"), + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + fail("allowed too many rules to be created"); + } catch (IllegalArgumentException e) { + // yay + } + } + + @Test + public void testAddAutomaticZenRule_beyondSystemLimit_differentComponents() { + // Make sure the system limit is enforced per-package even with different component provider + // names. + for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { + ScheduleInfo si = new ScheduleInfo(); + si.startHour = i; + AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, + null, + new ComponentName("android", "ScheduleConditionProvider" + i), + ZenModeConfig.toScheduleConditionId(si), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + assertNotNull(id); + } + try { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + null, + new ComponentName("android", "ScheduleConditionProviderFinal"), + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + fail("allowed too many rules to be created"); + } catch (IllegalArgumentException e) { + // yay + } + } + + @Test + public void testAddAutomaticZenRule_claimedSystemOwner() { + // Make sure anything that claims to have a "system" owner but not actually part of the + // system package still gets limited on number of rules + for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { + ScheduleInfo si = new ScheduleInfo(); + si.startHour = i; + AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, + new ComponentName("android", "ScheduleConditionProvider" + i), + null, // configuration activity + ZenModeConfig.toScheduleConditionId(si), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + assertNotNull(id); + } + try { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + new ComponentName("android", "ScheduleConditionProviderFinal"), + null, // configuration activity + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + fail("allowed too many rules to be created"); + } catch (IllegalArgumentException e) { + // yay + } + } + private void setupZenConfig() { mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConfig.allowAlarms = false; From 536a81886b6d3f1cef684c120918a5439c242ca1 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Wed, 4 Jan 2023 05:00:37 +0000 Subject: [PATCH 116/208] Revert "[RESTRICT AUTOMERGE] Trim the activity info of another uid if no privilege" This reverts commit 17df433a3d96fb6c00dc7078fd0f8048645ace6a. Reason for revert: apps crashed due to the top activity info trimmed Bug: 264269392 263434196 263438172 Change-Id: I078080b14b7cf7c6e605739f22f40f86802d3950 (cherry picked from commit 5caf2dde3d264966e1ba0dd3e18a0524858157ba) Merged-In: I078080b14b7cf7c6e605739f22f40f86802d3950 --- .../com/android/server/wm/AppTaskImpl.java | 2 +- .../com/android/server/wm/RecentTasks.java | 8 ++----- .../com/android/server/wm/RunningTasks.java | 4 ---- .../core/java/com/android/server/wm/Task.java | 21 ------------------- 4 files changed, 3 insertions(+), 32 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 2fd5963d653b..dd1d55b2d54d 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -84,7 +84,7 @@ public ActivityManager.RecentTaskInfo getTaskInfo() { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } return mService.getRecentTasks().createRecentTaskInfo(task, - false /* stripExtras */, true /* getTasksAllowed */); + false /* stripExtras */); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 6151b1892fda..3fe75a4ab49e 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -962,7 +962,7 @@ private ArrayList getRecentTasksImpl(int maxNum, continue; } - res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed)); + res.add(createRecentTaskInfo(task, true /* stripExtras */)); } return res; } @@ -1834,16 +1834,12 @@ void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { /** * Creates a new RecentTaskInfo from a Task. */ - ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras, - boolean getTasksAllowed) { + ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) { ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); tr.fillTaskInfo(rti, stripExtras); // Fill in some deprecated values rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; - if (!getTasksAllowed) { - Task.trimIneffectiveInfo(tr, rti); - } return rti; } diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 3c7917bb05d1..3509ba72d058 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -129,10 +129,6 @@ private RunningTaskInfo createRunningTaskInfo(Task task) { final RunningTaskInfo rti = task.getTaskInfo(); // Fill in some deprecated values rti.id = rti.taskId; - - if (!mAllowed) { - Task.trimIneffectiveInfo(task, rti); - } return rti; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c0e4262863e6..228b5054c13f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3653,27 +3653,6 @@ void fillTaskInfo(TaskInfo info, boolean stripExtras) { : SCREEN_ORIENTATION_UNSET; } - /** - * Removes the activity info if the activity belongs to a different uid, which is - * different from the app that hosts the task. - */ - static void trimIneffectiveInfo(Task task, TaskInfo info) { - final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing, - false /* traverseTopToBottom */); - final int baseActivityUid = - baseActivity != null ? baseActivity.getUid() : task.effectiveUid; - - if (info.topActivityInfo != null - && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) { - info.topActivity = null; - info.topActivityInfo = null; - } - - if (task.effectiveUid != baseActivityUid) { - info.baseActivity = null; - } - } - /** * Returns a {@link TaskInfo} with information from this task. */ From bfc32efc49a307dfa44d7dbc4e383097075246e0 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 16 May 2022 15:28:24 -0400 Subject: [PATCH 117/208] Move service initialization Occasionally ILockSettings can fail to be initialized otherwise Fixes: 232714129 Test: boot (and eventually bootstress/reboot-long) Change-Id: I2f9f9bdba37f4ebfaea56c1a6662f0474ae8a002 Merged-In: I2f9f9bdba37f4ebfaea56c1a6662f0474ae8a002 (cherry picked from commit 8e278543bd290d4b6c417758554d6dee93a4fe74) (cherry picked from commit caa5a22ea0c401c4eef548fb8161820beda3ff13) Merged-In: I2f9f9bdba37f4ebfaea56c1a6662f0474ae8a002 --- .../server/notification/NotificationManagerService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f6ead70b446d..346d586bab38 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1776,7 +1776,6 @@ public synchronized void onStrongAuthRequiredChanged(int userId) { } } - private LockPatternUtils mLockPatternUtils; private StrongAuthTracker mStrongAuthTracker; public NotificationManagerService(Context context) { @@ -1996,7 +1995,6 @@ void init(WorkerHandler handler, RankingHandler rankingHandler, ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); - mLockPatternUtils = new LockPatternUtils(getContext()); mStrongAuthTracker = new StrongAuthTracker(getContext()); String[] extractorNames; try { @@ -2445,7 +2443,7 @@ public void onBootPhase(int phase) { bubbsExtractor.setShortcutHelper(mShortcutHelper); } registerNotificationPreferencesPullers(); - mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); + new LockPatternUtils(getContext()).registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to // bind to listener services. From 4b17f4d01b89acf82d6ec206728125d3e5e0909a Mon Sep 17 00:00:00 2001 From: Alex Johnston Date: Tue, 5 Oct 2021 11:44:37 +0000 Subject: [PATCH 118/208] Stop managed profile owner granting READ_SMS Reason: There is only one telephony stack shared between the personal and work profile. Bug: 194382185 Bug: 189942529 Test: build Change-Id: If0d27a317a7c0ee46af371b30208327e5636c7cf (cherry picked from commit 87f37319bf7ee22c6e7c29432b6c9bbce0fdb591) Merged-In: If0d27a317a7c0ee46af371b30208327e5636c7cf --- core/java/android/app/admin/DevicePolicyManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c2f00215dd3d..aa6d74fb8c39 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9661,6 +9661,15 @@ public int getPermissionPolicy(ComponentName admin) { * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted. * + * Control over the following permissions are restricted for managed profile owners: + *

    + *
  • Manifest.permission.READ_SMS
  • + *
+ *

+ * A managed profile owner may not grant these permissions (i.e. call this method with any of + * the permissions listed above and {@code grantState} of + * {@code #PERMISSION_GRANT_STATE_GRANTED}), but may deny them. + * * @param admin Which profile or device owner this request is associated with. * @param packageName The application to grant or revoke a permission to. * @param permission The permission to grant or revoke. From 6cee00a9d81cfcf1e7d27bbb9238d504a8f06a7e Mon Sep 17 00:00:00 2001 From: Wenhao Wang Date: Tue, 30 Aug 2022 11:09:46 -0700 Subject: [PATCH 119/208] Enable user graularity for lockdown mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NotificationManagerService registers a LockPatternUtils.StrongAuthTracker to observe the StrongAuth changes of every user. More specifically, it’s the STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN flag. Via this flag, NotificationManagerService can perform the following operations when the user enter or exit lockdown mode: Enter lockdown: 1. Remove all the notifications belonging to the user. 2. Set the local flag to indicate the lockdown is on for the user. The local flag will suppress the user's notifications on the post, remove and update functions. Exit lockdown: 1. Clear the local flag to indicate the lockdown is off for the user. 2. Repost the user’s notifications (suppressed during lockdown mode). The CL also updates corresponding tests. Bug: 173721373 Bug: 250743174 Test: atest NotificationManagerServiceTest Test: atest NotificationListenersTest Ignore-AOSP-First: pending fix for a security issue. Change-Id: I4f30e56550729db7d673a92d2a1250509713f36d Merged-In: I4f30e56550729db7d673a92d2a1250509713f36d (cherry picked from commit de3b12fca23178d8c821058261572449b67d5967) (cherry picked from commit 5e40f39f5bd4ae769d79ce022a64f1345512b65d) Merged-In: I4f30e56550729db7d673a92d2a1250509713f36d --- .../NotificationManagerService.java | 75 +++++---- .../NotificationListenersTest.java | 150 ++++++++++++------ .../NotificationManagerServiceTest.java | 72 ++++++++- 3 files changed, 209 insertions(+), 88 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 346d586bab38..14454596db3d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1744,34 +1744,39 @@ private boolean containsFlag(int haystack, int needle) { return (haystack & needle) != 0; } - public boolean isInLockDownMode() { - return mIsInLockDownMode; + // Return whether the user is in lockdown mode. + // If the flag is not set, we assume the user is not in lockdown. + public boolean isInLockDownMode(int userId) { + return mUserInLockDownMode.get(userId, false); } @Override public synchronized void onStrongAuthRequiredChanged(int userId) { boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mUserInLockDownMode.put(userId, userInLockDownModeNext); - boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; - if (mIsInLockDownMode == isInLockDownModeNext) { + // Nothing happens if the lockdown mode of userId keeps the same. + if (userInLockDownModeNext == isInLockDownMode(userId)) { return; } - if (isInLockDownModeNext) { - cancelNotificationsWhenEnterLockDownMode(); + // When the lockdown mode is changed, we perform the following steps. + // If the userInLockDownModeNext is true, all the function calls to + // notifyPostedLocked and notifyRemovedLocked will not be executed. + // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked + // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked. + // So we shall call cancelNotificationsWhenEnterLockDownMode before + // we set mUserInLockDownMode as true. + // On the other hand, if the userInLockDownModeNext is false, we shall call + // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode + if (userInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(userId); } - // When the mIsInLockDownMode is true, both notifyPostedLocked and - // notifyRemovedLocked will be dismissed. So we shall call - // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode - // as true and call postNotificationsWhenExitLockDownMode after we set - // mIsInLockDownMode as false. - mIsInLockDownMode = isInLockDownModeNext; + mUserInLockDownMode.put(userId, userInLockDownModeNext); - if (!isInLockDownModeNext) { - postNotificationsWhenExitLockDownMode(); + if (!userInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(userId); } } } @@ -8704,11 +8709,14 @@ protected void unhideNotificationsForPackages(String[] pkgs) { } } - private void cancelNotificationsWhenEnterLockDownMode() { + private void cancelNotificationsWhenEnterLockDownMode(int userId) { synchronized (mNotificationLock) { int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); + if (rec.getUser().getIdentifier() != userId) { + continue; + } mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, rec.getStats()); } @@ -8716,14 +8724,23 @@ private void cancelNotificationsWhenEnterLockDownMode() { } } - private void postNotificationsWhenExitLockDownMode() { + private void postNotificationsWhenExitLockDownMode(int userId) { synchronized (mNotificationLock) { int numNotifications = mNotificationList.size(); + // Set the delay to spread out the burst of notifications. + long delay = 0; for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); - mListeners.notifyPostedLocked(rec, rec); + if (rec.getUser().getIdentifier() != userId) { + continue; + } + mHandler.postDelayed(() -> { + synchronized (mNotificationLock) { + mListeners.notifyPostedLocked(rec, rec); + } + }, delay); + delay += 20; } - } } @@ -8930,12 +8947,15 @@ private static String callStateToString(int state) { * notifications visible to the given listener. */ @GuardedBy("mNotificationLock") - private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { + NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { final int N = mNotificationList.size(); final ArrayList rankings = new ArrayList<>(); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); + if (isInLockDownMode(record.getUser().getIdentifier())) { + continue; + } if (!isVisibleToListener(record.getSbn(), info)) { continue; } @@ -8974,8 +8994,8 @@ private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo inf rankings.toArray(new NotificationListenerService.Ranking[0])); } - boolean isInLockDownMode() { - return mStrongAuthTracker.isInLockDownMode(); + boolean isInLockDownMode(int userId) { + return mStrongAuthTracker.isInLockDownMode(userId); } boolean hasCompanionDevice(ManagedServiceInfo info) { @@ -9010,7 +9030,8 @@ protected ICompanionDeviceManager getCompanionManager() { ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)); } - private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { + @VisibleForTesting + boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { if (!listener.enabledAndUserMatches(sbn.getUserId())) { return false; } @@ -9696,7 +9717,7 @@ public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) { @GuardedBy("mNotificationLock") void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -9796,7 +9817,7 @@ private void updateUriPermissionsForActiveNotificationsLocked( @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -9845,10 +9866,6 @@ public void notifyRemovedLocked(NotificationRecord r, int reason, */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List changedHiddenNotifications) { - if (isInLockDownMode()) { - return; - } - boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index 7244fcdda731..a4727a09570c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -28,6 +28,7 @@ import android.app.INotificationManager; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.os.UserHandle; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.testing.TestableContext; @@ -40,8 +41,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.internal.util.reflection.FieldSetter; -import java.util.List; - public class NotificationListenersTest extends UiServiceTestCase { @Mock @@ -71,63 +70,112 @@ public void setUp() throws Exception { @Test public void testNotifyPostedLockedInLockdownMode() { - NotificationRecord r = mock(NotificationRecord.class); - NotificationRecord old = mock(NotificationRecord.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, atLeast(2)).getSbn(); - - // in the lockdown mode - reset(r); - reset(old); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, never()).getSbn(); - } - - @Test - public void testnotifyRankingUpdateLockedInLockdownMode() { - List chn = mock(List.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, atLeast(1)).size(); - - // in the lockdown mode - reset(chn); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, never()).size(); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationRecord old0 = mock(NotificationRecord.class); + UserHandle uh0 = mock(UserHandle.class); + + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationRecord old1 = mock(NotificationRecord.class); + UserHandle uh1 = mock(UserHandle.class); + + // Neither user0 and user1 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(false); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + + mListeners.notifyPostedLocked(r0, old0, true); + mListeners.notifyPostedLocked(r0, old0, false); + verify(r0, atLeast(2)).getSbn(); + + mListeners.notifyPostedLocked(r1, old1, true); + mListeners.notifyPostedLocked(r1, old1, false); + verify(r1, atLeast(2)).getSbn(); + + // Reset + reset(r0); + reset(old0); + reset(r1); + reset(old1); + + // Only user 0 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(true); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + + mListeners.notifyPostedLocked(r0, old0, true); + mListeners.notifyPostedLocked(r0, old0, false); + verify(r0, never()).getSbn(); + + mListeners.notifyPostedLocked(r1, old1, true); + mListeners.notifyPostedLocked(r1, old1, false); + verify(r1, atLeast(2)).getSbn(); } @Test public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { - NotificationRecord r = mock(NotificationRecord.class); - NotificationStats rs = mock(NotificationStats.class); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationStats rs0 = mock(NotificationStats.class); + UserHandle uh0 = mock(UserHandle.class); + + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationStats rs1 = mock(NotificationStats.class); + UserHandle uh1 = mock(UserHandle.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mHandler"), mock(NotificationManagerService.WorkerHandler.class)); - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, atLeast(2)).getSbn(); - - // in the lockdown mode - reset(r); - reset(rs); - when(mNm.isInLockDownMode()).thenReturn(true); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, never()).getSbn(); + // Neither user0 and user1 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(false); + when(r0.getSbn()).thenReturn(sbn); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + when(r1.getSbn()).thenReturn(sbn); + + mListeners.notifyRemovedLocked(r0, 0, rs0); + mListeners.notifyRemovedLocked(r0, 0, rs0); + verify(r0, atLeast(2)).getSbn(); + + mListeners.notifyRemovedLocked(r1, 0, rs1); + mListeners.notifyRemovedLocked(r1, 0, rs1); + verify(r1, atLeast(2)).getSbn(); + + // Reset + reset(r0); + reset(rs0); + reset(r1); + reset(rs1); + + // Only user 0 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(true); + when(r0.getSbn()).thenReturn(sbn); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + when(r1.getSbn()).thenReturn(sbn); + + mListeners.notifyRemovedLocked(r0, 0, rs0); + mListeners.notifyRemovedLocked(r0, 0, rs0); + verify(r0, never()).getSbn(); + + mListeners.notifyRemovedLocked(r1, 0, rs1); + mListeners.notifyRemovedLocked(r1, 0, rs1); + verify(r1, atLeast(2)).getSbn(); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 6a8815faa9e6..49ad501d90e1 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -141,6 +141,7 @@ import android.service.notification.Adjustment; import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; @@ -174,6 +175,7 @@ import com.android.server.UiServiceTestCase; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; +import com.android.server.notification.ManagedServices.ManagedServiceInfo; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.pm.PackageManagerService; @@ -304,6 +306,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { StatusBarManagerInternal mStatusBar; private final FakeSystemClock mSystemClock = new FakeSystemClock(); + private NotificationManagerService.WorkerHandler mWorkerHandler; + // Use a Testable subclass so we can simulate calls from the system without failing. private static class TestableNotificationManagerService extends NotificationManagerService { int countSystemChecks = 0; @@ -317,6 +321,9 @@ private static class TestableNotificationManagerService extends NotificationMana @Nullable NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; + @Nullable + Boolean mIsVisibleToListenerReturnValue = null; + TestableNotificationManagerService( Context context, NotificationRecordLogger logger, @@ -409,6 +416,18 @@ interface NotificationAssistantAccessGrantedCallback { void onGranted(ComponentName assistant, int userId, boolean granted); } + protected void setIsVisibleToListenerReturnValue(boolean value) { + mIsVisibleToListenerReturnValue = value; + } + + @Override + boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { + if (mIsVisibleToListenerReturnValue != null) { + return mIsVisibleToListenerReturnValue; + } + return super.isVisibleToListener(sbn, listener); + } + class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { private int mGetStrongAuthForUserReturnValue = 0; StrongAuthTrackerFake(Context context) { @@ -531,7 +550,8 @@ null, new ComponentName(PKG, "test_class"), when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true); - mService.init(mService.new WorkerHandler(mTestableLooper.getLooper()), + mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper())); + mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient, mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, @@ -600,6 +620,7 @@ public void tearDown() throws Exception { mService.unregisterDeviceConfigChange(); InstrumentationRegistry.getInstrumentation() .getUiAutomation().dropShellPermissionIdentity(); + mWorkerHandler.removeCallbacksAndMessages(null); } private ArrayMap> generateResetComponentValues() { @@ -7290,10 +7311,10 @@ public void testStrongAuthTracker_isInLockDownMode() { mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); - mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId())); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId()); mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertFalse(mStrongAuthTracker.isInLockDownMode()); + assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId())); } @Test @@ -7309,8 +7330,8 @@ public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { // when entering the lockdown mode, cancel the 2 notifications. mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(0)); // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN. ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); @@ -7319,9 +7340,44 @@ public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { // exit lockdown mode. mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertFalse(mStrongAuthTracker.isInLockDownMode(0)); // the notifyPostedLocked function is called twice. - verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); + } + + @Test + public void testMakeRankingUpdateLockedInLockDownMode() { + // post 2 notifications from a same package + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 1), mTestNotificationChannel); + mService.addNotification(pkgB); + + mService.setIsVisibleToListenerReturnValue(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null); + assertEquals(2, nru.getRankingMap().getOrderedKeys().length); + + // when only user 0 entering the lockdown mode, its notification will be suppressed. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(0)); + assertFalse(mStrongAuthTracker.isInLockDownMode(1)); + + nru = mService.makeRankingUpdateLocked(null); + assertEquals(1, nru.getRankingMap().getOrderedKeys().length); + + // User 0 exits lockdown mode. Its notification will be resumed. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertFalse(mStrongAuthTracker.isInLockDownMode(0)); + assertFalse(mStrongAuthTracker.isInLockDownMode(1)); + + nru = mService.makeRankingUpdateLocked(null); + assertEquals(2, nru.getRankingMap().getOrderedKeys().length); } } \ No newline at end of file From 0e521bdffc200fe5bbbf3721713ace6f28bf810b Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Fri, 2 Dec 2022 09:44:31 -0800 Subject: [PATCH 120/208] RESTRICT AUTOMERGE Revoke dev perm if app is upgrading to post 23 and perm has pre23 flag If a permission has the "pre23" flag, and an app is upgrading past api 23, then we should not assume that a "development" permission remains granted Fixes: 259458532 Test: atest RevokeSawPermissionTest Change-Id: I214396f455c5ed9e8bac2e50b1525b86475c81c7 (cherry picked from commit 2f30a63b11e59f9daf42f51eb85aa91c86f4baf4) Merged-In: I214396f455c5ed9e8bac2e50b1525b86475c81c7 --- .../pm/permission/PermissionManagerService.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 0a98b8b0df57..f83c3d5145cc 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2347,7 +2347,7 @@ private void revokeSystemAlertWindowIfUpgradedPast23( final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(newPackage.getPackageName()); if (grantSignaturePermission(Manifest.permission.SYSTEM_ALERT_WINDOW, newPackage, ps, saw, - ps.getPermissionsState())) { + ps.getPermissionsState(), true)) { return; } for (int userId : mUserManagerInt.getUserIds()) { @@ -3596,6 +3596,13 @@ private boolean hasPrivappWhitelistEntry(String perm, AndroidPackage pkg) { private boolean grantSignaturePermission(String perm, AndroidPackage pkg, PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) { + return grantSignaturePermission(perm, pkg, pkgSetting, bp, origPermissions, false); + } + + + private boolean grantSignaturePermission(String perm, AndroidPackage pkg, + PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions, + boolean isApi23Upgrade) { boolean oemPermission = bp.isOEM(); boolean vendorPrivilegedPermission = bp.isVendorPrivileged(); boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged(); @@ -3770,7 +3777,7 @@ && canGrantOemPermission(disabledPs, perm)))) { // Any pre-installed system app is allowed to get this permission. allowed = true; } - if (!allowed && bp.isDevelopment()) { + if (!allowed && bp.isDevelopment() && !(bp.isPre23() && isApi23Upgrade)) { // For development permissions, a development permission // is granted only if it was already granted. allowed = origPermissions.hasInstallPermission(perm); From 636d60d9800be8ba6709e1d3ae35fa1d9e0953f1 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 26 Sep 2022 20:37:33 +0100 Subject: [PATCH 121/208] Reconcile WorkSource parcel and unparcel code. Prior to this CL, WorkSources would Parcel their list of WorkChains as -1 if null, or the size of the list followed by the list itself if non-null. When reading it back in, on the other hand, they would check if the size was positive, and only then read the list from the Parcel. This works for all cases except when the WorkSource has an empty but non-null list of WorkChains as the list would get written to the parcel, but then never read on the other side. If parceling a list was a no-op when empty this wouldn't be an issue, but it must write at least its size into the parcel to know how many elements to extract. In the empty list case, this single element is left unread as the size is not positive which essentially corrupts any future items read from that same parcelable. Bug: 220302519 Test: atest android.security.cts.WorkSourceTest#testWorkChainParceling Change-Id: I2fec40dfced420ca38e717059b0e95ee8ef9946a (cherry picked from commit 266b3bddcf14d448c0972db64b42950f76c759e3) Merged-In: I2fec40dfced420ca38e717059b0e95ee8ef9946a --- core/java/android/os/WorkSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index 6adba63f42ce..485d43066b0f 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -129,7 +129,7 @@ public WorkSource(int uid, @NonNull String packageName) { mNames = in.createStringArray(); int numChains = in.readInt(); - if (numChains > 0) { + if (numChains >= 0) { mChains = new ArrayList<>(numChains); in.readParcelableList(mChains, WorkChain.class.getClassLoader()); } else { From fa2cc4947b06cc0e5129295abc2bcd7c8907e86c Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 11 Jan 2023 18:58:41 +0000 Subject: [PATCH 122/208] Revert "Ensure that only SysUI can override pending intent launch flags" This reverts commit c4d3106e347922610f8c554de3ae238175ed393e. Reason for revert: b/264884187, b/264885689 Change-Id: I9fb0d66327f3f872a92e6b9d682d58489e81e6ba (cherry picked from commit 7bb933f48ff15d8f08d2185005b7b3e212915276) Merged-In: I9fb0d66327f3f872a92e6b9d682d58489e81e6ba --- .../com/android/server/am/PendingIntentRecord.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 818b70d61d89..1997dbd6fc37 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -326,16 +326,11 @@ public int sendInner(int code, Intent intent, String resolvedType, IBinder white resolvedType = key.requestResolvedType; } - // Apply any launch flags from the ActivityOptions. This is used only by SystemUI - // to ensure that we can launch the pending intent with a consistent launch mode even - // if the provided PendingIntent is immutable (ie. to force an activity to launch into - // a new task, or to launch multiple instances if supported by the app) + // Apply any launch flags from the ActivityOptions. This is to ensure that the caller + // can specify a consistent launch mode even if the PendingIntent is immutable final ActivityOptions opts = ActivityOptions.fromBundle(options); if (opts != null) { - // TODO(b/254490217): Move this check into SafeActivityOptions - if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) { - finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); - } + finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); } // Extract options before clearing calling identity From 4aaae273ec963a3bcae0094ecfdc0d958fdf834e Mon Sep 17 00:00:00 2001 From: Kunal Malhotra Date: Mon, 7 Nov 2022 23:33:55 +0000 Subject: [PATCH 123/208] Checking if package belongs to UID before registering broadcast receiver Test: manual testing done on device by installing test APK and checking if receiver can register Bug: 242040055 Change-Id: Ia525f218a46f8bf7fff660cec0d6432f09fdf24d Merged-In: Ia525f218a46f8bf7fff660cec0d6432f09fdf24d (cherry picked from commit 790a8d0dd329460bc60456681cb446accf2a27e0) (cherry picked from commit 4f0dc37b896e06086391e71ce471e413215e1130) Merged-In: Ia525f218a46f8bf7fff660cec0d6432f09fdf24d --- services/core/java/com/android/server/am/ActiveServices.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 25c4844a2984..7c81d2aaaf0b 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2517,6 +2517,11 @@ private ServiceLookupResult retrieveServiceLocked(Intent service, throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + className + " is not an isolatedProcess"); } + if (AppGlobals.getPackageManager().getPackageUid(callingPackage, + 0, userId) != callingUid) { + throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + + "calling package not owned by calling UID "); + } // Run the service under the calling package's application. ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo( callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId); From 5f7f5c01a321b4f98fae88aa51175d2beb1c1852 Mon Sep 17 00:00:00 2001 From: Hao Ke Date: Mon, 12 Dec 2022 15:49:16 +0000 Subject: [PATCH 124/208] Fix checkKeyIntentParceledCorrectly's bypass The checkKeyIntentParceledCorrectly method was added in checkKeyIntent, which was originaly only invoked when AccountManagerService deserializes the KEY_INTENT value as not NULL. However, due to the self-changing bundle technique in Parcel mismatch problems, the Intent value can change after reparceling; hence would bypass the added checkKeyIntentParceledCorrectly call. This CL did the following: - Ensure the checkKeyIntent method is also called when result.getParcelable(AccountManager.KEY_INTENT) == null. Bug: 260567867 Bug: 262230405 Test: local test, see b/262230405 Test: atest CtsAccountManagerTestCases Merged-In: I7b528f52c41767ae12731838fdd36aa26a8f3477 Change-Id: I7b528f52c41767ae12731838fdd36aa26a8f3477 (cherry picked from commit 9f623983a8d4ec48d58b0eda56fa461fc6748981) Merged-In: I7b528f52c41767ae12731838fdd36aa26a8f3477 --- .../server/accounts/AccountManagerService.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index ad41c129e96a..8d4337accc20 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3430,8 +3430,7 @@ public void onResult(Bundle result) { Bundle.setDefusable(result, true); mNumResults++; Intent intent = null; - if (result != null - && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { + if (result != null) { if (!checkKeyIntent( Binder.getCallingUid(), result)) { @@ -4790,8 +4789,10 @@ protected boolean checkKeyIntent(int authUid, Bundle bundle) { EventLog.writeEvent(0x534e4554, "250588548", authUid, ""); return false; } - Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + if (intent == null) { + return true; + } // Explicitly set an empty ClipData to ensure that we don't offer to // promote any Uris contained inside for granting purposes if (intent.getClipData() == null) { @@ -4844,7 +4845,10 @@ private boolean checkKeyIntentParceledCorrectly(Bundle bundle) { p.recycle(); Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT); - return (intent.filterEquals(simulateIntent)); + if (intent == null) { + return (simulateIntent == null); + } + return intent.filterEquals(simulateIntent); } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { @@ -4989,8 +4993,7 @@ public void onResult(Bundle result) { } } } - if (result != null - && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { + if (result != null) { if (!checkKeyIntent( Binder.getCallingUid(), result)) { From d328d7239b323688ff9cff8da813d955c57ab9a8 Mon Sep 17 00:00:00 2001 From: Winson Chiu Date: Fri, 6 Jan 2023 21:26:24 +0000 Subject: [PATCH 125/208] Encode Intent scheme when serializing to URI string RESTRICT AUTOMERGE Avoids deserialization error when the scheme contains a reserved character. Bug: 261858325 Test: atest android.content.cts.IntentTest#testEncoding Merged-In: Ic34b3f796b762763db5aa7b5d7c109ae70607470 Change-Id: Ic34b3f796b762763db5aa7b5d7c109ae70607470 (cherry picked from commit 43437b4ee6424933d4e403f0375ef8c1f07986f4) Merged-In: Ic34b3f796b762763db5aa7b5d7c109ae70607470 --- core/java/android/content/Intent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ededd0d2ea30..24d59a0826c8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -10750,7 +10750,7 @@ private void toUriFragment(StringBuilder uri, String scheme, String defAction, private void toUriInner(StringBuilder uri, String scheme, String defAction, String defPackage, int flags) { if (scheme != null) { - uri.append("scheme=").append(scheme).append(';'); + uri.append("scheme=").append(Uri.encode(scheme)).append(';'); } if (mAction != null && !mAction.equals(defAction)) { uri.append("action=").append(Uri.encode(mAction)).append(';'); From aef7c25af0bd16dec3f203559d6f59b57098e425 Mon Sep 17 00:00:00 2001 From: Christophe Pinelli Date: Tue, 27 Dec 2022 20:29:33 +0000 Subject: [PATCH 126/208] [DO NOT MERGE] Backport BAL restrictions from S to R, this blocks apps from using Alarm Manager to bypass BAL restrictions. Test: atest BackgroundActivityLaunchTest Bug: 195756028 Change-Id: Ifa3f79bc74c10d0ac8322079f2e6e3e0ba476b0f (cherry picked from commit 1d737c2fbdc71570bbcaca0f44da4ee132fa545f) Merged-In: Ifa3f79bc74c10d0ac8322079f2e6e3e0ba476b0f --- core/java/android/app/ActivityOptions.java | 10 +-- core/java/android/app/BroadcastOptions.java | 25 +++++- core/java/android/app/ComponentOptions.java | 84 +++++++++++++++++++ .../android/server/AlarmManagerService.java | 21 ++++- .../server/am/PendingIntentRecord.java | 22 ++++- .../android/server/wm/ActivityStarter.java | 22 +++-- .../server/wm/ActivityTaskManagerService.java | 2 +- .../com/android/server/wm/AppTaskImpl.java | 2 +- 8 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 core/java/android/app/ComponentOptions.java diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 4aedfeefb72b..c9723bd4fce3 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -61,7 +61,7 @@ * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) * Context.startActivity(Intent, Bundle)} and related methods. */ -public class ActivityOptions { +public class ActivityOptions extends ComponentOptions { private static final String TAG = "ActivityOptions"; /** @@ -963,13 +963,12 @@ public boolean getLaunchTaskBehind() { } private ActivityOptions() { + super(); } /** @hide */ public ActivityOptions(Bundle opts) { - // If the remote side sent us bad parcelables, they won't get the - // results they want, which is their loss. - opts.setDefusable(true); + super(opts); mPackageName = opts.getString(KEY_PACKAGE_NAME); try { @@ -1575,8 +1574,9 @@ public void update(ActivityOptions otherOptions) { * object; you must not modify it, but can supply it to the startActivity * methods that take an options Bundle. */ + @Override public Bundle toBundle() { - Bundle b = new Bundle(); + Bundle b = super.toBundle(); if (mPackageName != null) { b.putString(KEY_PACKAGE_NAME, mPackageName); } diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 161e2ad06ec0..8947fb44b07b 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -28,7 +28,7 @@ * {@hide} */ @SystemApi -public class BroadcastOptions { +public class BroadcastOptions extends ComponentOptions { private long mTemporaryAppWhitelistDuration; private int mMinManifestReceiverApiLevel = 0; private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; @@ -72,10 +72,12 @@ public static BroadcastOptions makeBasic() { } private BroadcastOptions() { + super(); } /** @hide */ public BroadcastOptions(Bundle opts) { + super(opts); mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION); mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0); mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, @@ -173,6 +175,24 @@ public boolean allowsBackgroundActivityStarts() { return mAllowBackgroundActivityStarts; } + /** + * Set PendingIntent activity is allowed to be started in the background if the caller + * can start background activities. + * @hide + */ + public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) { + super.setPendingIntentBackgroundActivityLaunchAllowed(allowed); + } + + /** + * Get PendingIntent activity is allowed to be started in the background if the caller + * can start background activities. + * @hide + */ + public boolean isPendingIntentBackgroundActivityLaunchAllowed() { + return super.isPendingIntentBackgroundActivityLaunchAllowed(); + } + /** * Returns the created options as a Bundle, which can be passed to * {@link android.content.Context#sendBroadcast(android.content.Intent) @@ -181,8 +201,9 @@ public boolean allowsBackgroundActivityStarts() { * object; you must not modify it, but can supply it to the sendBroadcast * methods that take an options Bundle. */ + @Override public Bundle toBundle() { - Bundle b = new Bundle(); + Bundle b = super.toBundle(); if (mTemporaryAppWhitelistDuration > 0) { b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration); } diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java new file mode 100644 index 000000000000..34ee9138a364 --- /dev/null +++ b/core/java/android/app/ComponentOptions.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.os.Bundle; + +/** + * @hide + */ +public class ComponentOptions { + + /** + * Default value for KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED. + * @hide + **/ + public static final boolean PENDING_INTENT_BAL_ALLOWED_DEFAULT = true; + + /** + * PendingIntent caller allows activity start even if PendingIntent creator is in background. + * This only works if the PendingIntent caller is allowed to start background activities, + * for example if it's in the foreground, or has BAL permission. + * @hide + */ + public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED = + "android.pendingIntent.backgroundActivityAllowed"; + + private boolean mPendingIntentBalAllowed = PENDING_INTENT_BAL_ALLOWED_DEFAULT; + + ComponentOptions() { + } + + ComponentOptions(Bundle opts) { + // If the remote side sent us bad parcelables, they won't get the + // results they want, which is their loss. + opts.setDefusable(true); + setPendingIntentBackgroundActivityLaunchAllowed( + opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, + PENDING_INTENT_BAL_ALLOWED_DEFAULT)); + } + + /** + * Set PendingIntent activity is allowed to be started in the background if the caller + * can start background activities. + * + * @hide + */ + public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) { + mPendingIntentBalAllowed = allowed; + } + + /** + * Get PendingIntent activity is allowed to be started in the background if the caller + * can start background activities. + * + * @hide + */ + public boolean isPendingIntentBackgroundActivityLaunchAllowed() { + return mPendingIntentBalAllowed; + } + + /** + * @hide + */ + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, + mPendingIntentBalAllowed); + return bundle; + } +} diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index b113a6196491..7cbe0c8ba57e 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -28,6 +28,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.BroadcastOptions; @@ -271,6 +272,8 @@ interface Stats { * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE. */ Bundle mIdleOptions; + ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic(); + BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic(); private final SparseArray mNextAlarmClockForUser = new SparseArray<>(); @@ -487,6 +490,7 @@ public void updateAllowWhileIdleWhitelistDurationLocked() { mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION; BroadcastOptions opts = BroadcastOptions.makeBasic(); opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION); + opts.setPendingIntentBackgroundActivityLaunchAllowed(false); mIdleOptions = opts.toBundle(); } } @@ -1481,6 +1485,8 @@ public void dumpDebug(ProtoOutputStream proto, long fieldId) { @Override public void onStart() { mInjector.init(); + mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false); + mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false); mListenerDeathRecipient = new IBinder.DeathRecipient() { @Override @@ -4165,6 +4171,13 @@ private static int getAlarmAttributionUid(Alarm alarm) { return alarm.creatorUid; } + private Bundle getAlarmOperationBundle(Alarm alarm) { + if (alarm.operation.isActivity()) { + return mActivityOptsRestrictBal.toBundle(); + } + return mBroadcastOptsRestrictBal.toBundle(); + } + @VisibleForTesting class AlarmHandler extends Handler { public static final int ALARM_EVENT = 1; @@ -4203,7 +4216,11 @@ public void handleMessage(Message msg) { for (int i=0; i 0) { // This IntentSender is no longer valid, but this @@ -4739,7 +4756,7 @@ public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) mBackgroundIntent.putExtra( Intent.EXTRA_ALARM_COUNT, alarm.count), mDeliveryTracker, mHandler, null, - allowWhileIdle ? mIdleOptions : null); + allowWhileIdle ? mIdleOptions : getAlarmOperationBundle(alarm)); } catch (PendingIntent.CanceledException e) { if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 1997dbd6fc37..52379d5f9ab0 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -286,6 +286,25 @@ public int sendWithResult(int code, Intent intent, String resolvedType, IBinder requiredPermission, null, null, 0, 0, 0, options); } + /** + * Return true if the activity options allows PendingIntent to use caller's BAL permission. + */ + public static boolean isPendingIntentBalAllowedByCaller( + @Nullable ActivityOptions activityOptions) { + if (activityOptions == null) { + return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT; + } + return isPendingIntentBalAllowedByCaller(activityOptions.toBundle()); + } + + private static boolean isPendingIntentBalAllowedByCaller(@Nullable Bundle options) { + if (options == null) { + return ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT; + } + return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, + ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT); + } + public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { @@ -398,7 +417,8 @@ public int sendInner(int code, Intent intent, String resolvedType, IBinder white // temporarily allow receivers and services to open activities from background if the // PendingIntent.send() caller was foreground at the time of sendInner() call final boolean allowTrampoline = uid != callingUid - && controller.mAtmInternal.isUidForeground(callingUid); + && controller.mAtmInternal.isUidForeground(callingUid) + && isPendingIntentBalAllowedByCaller(options); // note: we on purpose don't pass in the information about the PendingIntent's creator, // like pid or ProcessRecord, to the ActivityTaskManagerInternal calls below, because diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 54ad4acaca4c..5d4549631572 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1004,6 +1004,10 @@ private int executeRequest(Request request) { abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, callingPackage); + // Merge the two options bundles, while realCallerOptions takes precedence. + ActivityOptions checkedOptions = options != null + ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; + boolean restrictedBgActivity = false; if (!abort) { try { @@ -1012,15 +1016,12 @@ private int executeRequest(Request request) { restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, request.originatingPendingIntent, request.allowBackgroundActivityStart, - intent); + intent, checkedOptions); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } - // Merge the two options bundles, while realCallerOptions takes precedence. - ActivityOptions checkedOptions = options != null - ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; if (request.allowPendingRemoteAnimationRegistryLookup) { checkedOptions = mService.getActivityStartController() .getPendingRemoteAnimationRegistry() @@ -1234,7 +1235,7 @@ private void onExecutionComplete() { boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - boolean allowBackgroundActivityStart, Intent intent) { + boolean allowBackgroundActivityStart, Intent intent, ActivityOptions checkedOptions) { // don't abort for the most important UIDs final int callingAppId = UserHandle.getAppId(callingUid); if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID @@ -1278,7 +1279,11 @@ boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, ? isCallingUidPersistentSystemProcess : (realCallingAppId == Process.SYSTEM_UID) || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; - if (realCallingUid != callingUid) { + // Legacy behavior allows to use caller foreground state to bypass BAL restriction. + final boolean balAllowedByPiSender = + PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions); + + if (balAllowedByPiSender && realCallingUid != callingUid) { // don't abort if the realCallingUid has a visible window if (realCallingUidHasAnyVisibleWindow) { if (DEBUG_ACTIVITY_STARTS) { @@ -1346,9 +1351,10 @@ boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, // If we don't have callerApp at this point, no caller was provided to startActivity(). // That's the case for PendingIntent-based starts, since the creator's process might not be // up and alive. If that's the case, we retrieve the WindowProcessController for the send() - // caller, so that we can make the decision based on its foreground/whitelisted state. + // caller if caller allows, so that we can make the decision + // based on its foreground/whitelisted state. int callerAppUid = callingUid; - if (callerApp == null) { + if (callerApp == null && balAllowedByPiSender) { callerApp = mService.getProcessController(realCallingPid, realCallingUid); callerAppUid = realCallingUid; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index ef9a6f27fdde..4ec53a584e8c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2532,7 +2532,7 @@ void moveTaskToFrontLocked(@Nullable IApplicationThread appThread, final ActivityStarter starter = getActivityStartController().obtainStarter( null /* intent */, "moveTaskToFront"); if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1, - -1, callerApp, null, false, null)) { + -1, callerApp, null, false, null, null)) { if (!isBackgroundActivityStartsEnabled()) { return; } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index dd1d55b2d54d..828bb08cfab1 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -112,7 +112,7 @@ public void moveToFront(IApplicationThread appThread, String callingPackage) { final ActivityStarter starter = mService.getActivityStartController().obtainStarter( null /* intent */, "moveToFront"); if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, -1, -1, callerApp, null, false, null)) { + callingPackage, -1, -1, callerApp, null, false, null, null)) { if (!mService.isBackgroundActivityStartsEnabled()) { return; } From cee46be1e1c35a31ba0cbc780d58bf48a0e6d0e5 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Wed, 28 Sep 2022 06:46:29 +0000 Subject: [PATCH 127/208] [RESTRICT AUTOMERGE] Strip part of the activity info of another uid if no privilege The activity info could be from another uid which is different from the app that hosts the task. The information should be trimmed if the caller app doesn't have the privilege. However, removing the entire info may result in app compatibility issues. So, only swiping the info that are sensitive to empty string. Bug: 243130512 Test: verified market app locally Test: atest RecentTasksTest Change-Id: I5b6775dd3c4e2ccdacd30741884d336b2eaa70da Merged-In: I5b6775dd3c4e2ccdacd30741884d336b2eaa70da (cherry picked from commit 5ba72200f6a66b5da48c9c3abd103a73aea1ef95) (cherry picked from commit e534fa2a7119f39cb76f1d08557b7dcae3b6d94e) Merged-In: I5b6775dd3c4e2ccdacd30741884d336b2eaa70da --- .../com/android/server/wm/AppTaskImpl.java | 2 +- .../com/android/server/wm/RecentTasks.java | 8 +++- .../com/android/server/wm/RunningTasks.java | 4 ++ .../core/java/com/android/server/wm/Task.java | 48 +++++++++++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 828bb08cfab1..edd086df78aa 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -84,7 +84,7 @@ public ActivityManager.RecentTaskInfo getTaskInfo() { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } return mService.getRecentTasks().createRecentTaskInfo(task, - false /* stripExtras */); + false /* stripExtras */, true /* getTasksAllowed */); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 3fe75a4ab49e..6151b1892fda 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -962,7 +962,7 @@ private ArrayList getRecentTasksImpl(int maxNum, continue; } - res.add(createRecentTaskInfo(task, true /* stripExtras */)); + res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed)); } return res; } @@ -1834,12 +1834,16 @@ void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { /** * Creates a new RecentTaskInfo from a Task. */ - ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) { + ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras, + boolean getTasksAllowed) { ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); tr.fillTaskInfo(rti, stripExtras); // Fill in some deprecated values rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; + if (!getTasksAllowed) { + Task.trimIneffectiveInfo(tr, rti); + } return rti; } diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 3509ba72d058..3c7917bb05d1 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -129,6 +129,10 @@ private RunningTaskInfo createRunningTaskInfo(Task task) { final RunningTaskInfo rti = task.getTaskInfo(); // Fill in some deprecated values rti.id = rti.taskId; + + if (!mAllowed) { + Task.trimIneffectiveInfo(task, rti); + } return rti; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 228b5054c13f..a72763b97dbd 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3653,6 +3653,54 @@ void fillTaskInfo(TaskInfo info, boolean stripExtras) { : SCREEN_ORIENTATION_UNSET; } + /** + * Removes the activity info if the activity belongs to a different uid, which is + * different from the app that hosts the task. + */ + static void trimIneffectiveInfo(Task task, TaskInfo info) { + final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing, + false /* traverseTopToBottom */); + final int baseActivityUid = + baseActivity != null ? baseActivity.getUid() : task.effectiveUid; + + if (info.topActivityInfo != null + && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) { + // Making a copy to prevent eliminating the info in the original ActivityRecord. + info.topActivityInfo = new ActivityInfo(info.topActivityInfo); + info.topActivityInfo.applicationInfo = + new ApplicationInfo(info.topActivityInfo.applicationInfo); + + // Strip the sensitive info. + info.topActivity = new ComponentName("", ""); + info.topActivityInfo.packageName = ""; + info.topActivityInfo.taskAffinity = ""; + info.topActivityInfo.processName = ""; + info.topActivityInfo.name = ""; + info.topActivityInfo.parentActivityName = ""; + info.topActivityInfo.targetActivity = ""; + info.topActivityInfo.splitName = ""; + info.topActivityInfo.applicationInfo.className = ""; + info.topActivityInfo.applicationInfo.credentialProtectedDataDir = ""; + info.topActivityInfo.applicationInfo.dataDir = ""; + info.topActivityInfo.applicationInfo.deviceProtectedDataDir = ""; + info.topActivityInfo.applicationInfo.manageSpaceActivityName = ""; + info.topActivityInfo.applicationInfo.nativeLibraryDir = ""; + info.topActivityInfo.applicationInfo.nativeLibraryRootDir = ""; + info.topActivityInfo.applicationInfo.processName = ""; + info.topActivityInfo.applicationInfo.publicSourceDir = ""; + info.topActivityInfo.applicationInfo.scanPublicSourceDir = ""; + info.topActivityInfo.applicationInfo.scanSourceDir = ""; + info.topActivityInfo.applicationInfo.sourceDir = ""; + info.topActivityInfo.applicationInfo.taskAffinity = ""; + info.topActivityInfo.applicationInfo.name = ""; + info.topActivityInfo.applicationInfo.packageName = ""; + } + + if (task.effectiveUid != baseActivityUid) { + info.baseActivity = new ComponentName("", ""); + } + } + /** * Returns a {@link TaskInfo} with information from this task. */ From ebbd1f8969c9725e55c426d769dccc8239ca7ad2 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 18 Jan 2022 11:59:54 -0500 Subject: [PATCH 128/208] Add a limit on channel group creation Same as exists for channels This is a backport of the fix in ag/16659457, including the adjustment from ag/20920023 (changed the max value from 50000 to 6000). Test: PreferencesHelperTest Bug: 210114537 Bug: 261723753 Change-Id: Ic27efba4c54e22eebca16fc948879e652df4467b (cherry picked from commit 37b3549807d15452ac334fae316e615c3b9b8e8b & I3f3a99765c161369e1b026686a0e5f0c83ed839e) Merged-In: I3f3a99765c161369e1b026686a0e5f0c83ed839e Merged-In: Ic27efba4c54e22eebca16fc948879e652df4467b (cherry picked from commit 0f29716ab6fbf236e5d8f688bcdfdadf32429545) Merged-In: Ic27efba4c54e22eebca16fc948879e652df4467b --- .../notification/PreferencesHelper.java | 16 ++++++- .../notification/PreferencesHelperTest.java | 47 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 7a93edeaf664..8f969dc83a09 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -92,7 +92,9 @@ public class PreferencesHelper implements RankingConfig { private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":"; @VisibleForTesting - static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000; + static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000; + @VisibleForTesting + static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000; private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000; private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000; @@ -237,6 +239,7 @@ public void readXml(XmlPullParser parser, boolean forRestore, int userId) } } boolean skipWarningLogged = false; + boolean skipGroupWarningLogged = false; boolean hasSAWPermission = false; if (upgradeForBubbles && uid != UNKNOWN_UID) { hasSAWPermission = mAppOps.noteOpNoThrow( @@ -289,6 +292,14 @@ public void readXml(XmlPullParser parser, boolean forRestore, int userId) String tagName = parser.getName(); // Channel groups if (TAG_GROUP.equals(tagName)) { + if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) { + if (!skipGroupWarningLogged) { + Slog.w(TAG, "Skipping further groups for " + r.pkg + + "; app has too many"); + skipGroupWarningLogged = true; + } + continue; + } String id = parser.getAttributeValue(null, ATT_ID); CharSequence groupName = parser.getAttributeValue(null, ATT_NAME); @@ -790,6 +801,9 @@ public void createNotificationChannelGroup(String pkg, int uid, NotificationChan } if (fromTargetApp) { group.setBlocked(false); + if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) { + throw new IllegalStateException("Limit exceed; cannot create more groups"); + } } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (oldGroup != null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 06cfbea72a82..ffc12c94871e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -41,6 +41,7 @@ import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.UID_FIELD_NUMBER; import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE; import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT; +import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID; import static com.google.common.truth.Truth.assertThat; @@ -3074,6 +3075,52 @@ public void testTooManyChannels_xml() throws Exception { assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, extraChannel1, true)); } + @Test + public void testTooManyGroups() { + for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) { + NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i), + String.valueOf(i)); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + } + try { + NotificationChannelGroup group = new NotificationChannelGroup( + String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT), + String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT)); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + fail("Allowed to create too many notification channel groups"); + } catch (IllegalStateException e) { + // great + } + } + + @Test + public void testTooManyGroups_xml() throws Exception { + String extraGroup = "EXTRA"; + String extraGroup1 = "EXTRA1"; + + // create first... many... directly so we don't need a big xml blob in this test + for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) { + NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i), + String.valueOf(i)); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + } + + final String xml = "\n" + + "\n" + + "" + + "" + + "" + + ""; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNull(mHelper.getNotificationChannelGroup(extraGroup, PKG_O, UID_O)); + assertNull(mHelper.getNotificationChannelGroup(extraGroup1, PKG_O, UID_O)); + } + @Test public void testRestoreMultiUser() throws Exception { String pkg = "restore_pkg"; From afedc81e357a1847d297808459cff944e5ab0d52 Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Wed, 11 Jan 2023 16:02:27 +0800 Subject: [PATCH 129/208] [RESTRICT AUTOMERGE] Fix bypass BG-FGS and BAL via package manager APIs Opt-in for BAL of PendingIntent for following APIs: * PackageInstaller.uninstall() * PackageInstaller.installExistingPackage() * PackageInstaller.uninstallExistingPackage() * PackageInstaller.Session.commit() * PackageInstaller.Session.commitTransferred() * PackageManager.freeStorage() Bug: 230492955 Bug: 243377226 Test: atest android.security.cts.PackageInstallerTest Test: atest CtsStagedInstallHostTestCases Change-Id: I9b6f801d69ea6d2244a38dbe689e81afa4e798bf (cherry picked from commit b0b1ddb4b4ba5db27f5616b02ae2cdca8b63496f) Merged-In: I9b6f801d69ea6d2244a38dbe689e81afa4e798bf --- core/java/android/content/IntentSender.java | 42 ++++++++++++++++++- .../server/pm/PackageInstallerService.java | 11 ++++- .../server/pm/PackageInstallerSession.java | 34 ++++++++++++--- .../server/pm/PackageManagerService.java | 10 ++++- 4 files changed, 85 insertions(+), 12 deletions(-) diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index f40dc298d560..b34b72254743 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -16,7 +16,9 @@ package android.content; +import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.os.Handler; @@ -154,7 +156,7 @@ public void run() { */ public void sendIntent(Context context, int code, Intent intent, OnFinished onFinished, Handler handler) throws SendIntentException { - sendIntent(context, code, intent, onFinished, handler, null); + sendIntent(context, code, intent, onFinished, handler, null, null /* options */); } /** @@ -186,6 +188,42 @@ public void sendIntent(Context context, int code, Intent intent, public void sendIntent(Context context, int code, Intent intent, OnFinished onFinished, Handler handler, String requiredPermission) throws SendIntentException { + sendIntent(context, code, intent, onFinished, handler, requiredPermission, + null /* options */); + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * intent is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * @param requiredPermission Name of permission that a recipient of the PendingIntent + * is required to hold. This is only valid for broadcast intents, and + * corresponds to the permission argument in + * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}. + * If null, no permission is required. + * @param options Additional options the caller would like to provide to modify the sending + * behavior. May be built from an {@link ActivityOptions} to apply to an activity start. + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + * @hide + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler, String requiredPermission, + @Nullable Bundle options) + throws SendIntentException { try { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) @@ -195,7 +233,7 @@ public void sendIntent(Context context, int code, Intent intent, onFinished != null ? new FinishedDispatcher(this, onFinished, handler) : null, - requiredPermission, null); + requiredPermission, options); if (res < 0) { throw new SendIntentException(); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 3aa270790bd1..1cd0e8e86fe0 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -23,6 +23,7 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.Notification; import android.app.NotificationManager; import android.app.PackageDeleteObserver; @@ -1148,7 +1149,10 @@ public void onUserActionRequired(Intent intent) { PackageInstaller.STATUS_PENDING_USER_ACTION); fillIn.putExtra(Intent.EXTRA_INTENT, intent); try { - mTarget.sendIntent(mContext, 0, fillIn, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (SendIntentException ignored) { } } @@ -1173,7 +1177,10 @@ public void onPackageDeleted(String basePackageName, int returnCode, String msg) PackageManager.deleteStatusToString(returnCode, msg)); fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); try { - mTarget.sendIntent(mContext, 0, fillIn, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (SendIntentException ignored) { } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 016ee328fbee..94a13f9e7340 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -51,6 +51,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.BroadcastOptions; import android.app.Notification; import android.app.NotificationManager; import android.app.admin.DevicePolicyEventLogger; @@ -1300,13 +1301,21 @@ public void statusUpdate(Intent intent) { try { intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, PackageInstallerSession.this.sessionId); - mStatusReceiver.sendIntent(mContext, 0, intent, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + mStatusReceiver.sendIntent(mContext, 0, intent, + null /* onFinished*/, null /* handler */, + null /* requiredPermission */, options.toBundle()); } catch (IntentSender.SendIntentException ignore) { } } } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) { try { - mStatusReceiver.sendIntent(mContext, 0, intent, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + mStatusReceiver.sendIntent(mContext, 0, intent, + null /* onFinished*/, null /* handler */, + null /* requiredPermission */, options.toBundle()); } catch (IntentSender.SendIntentException ignore) { } } else { // failure, let's forward and clean up this session. @@ -1314,7 +1323,11 @@ public void statusUpdate(Intent intent) { PackageInstallerSession.this.sessionId); mChildSessionsRemaining.clear(); // we're done. Don't send any more. try { - mStatusReceiver.sendIntent(mContext, 0, intent, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + mStatusReceiver.sendIntent(mContext, 0, intent, + null /* onFinished*/, null /* handler */, + null /* requiredPermission */, options.toBundle()); } catch (IntentSender.SendIntentException ignore) { } } @@ -3260,7 +3273,10 @@ private static void sendOnUserActionRequired(Context context, IntentSender targe fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); fillIn.putExtra(Intent.EXTRA_INTENT, intent); try { - target.sendIntent(context, 0, fillIn, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + target.sendIntent(context, 0, fillIn, null /* onFinished */, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (IntentSender.SendIntentException ignored) { } } @@ -3300,7 +3316,10 @@ private static void sendOnPackageInstalled(Context context, IntentSender target, } } try { - target.sendIntent(context, 0, fillIn, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + target.sendIntent(context, 0, fillIn, null /* onFinished */, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (IntentSender.SendIntentException ignored) { } } @@ -3326,7 +3345,10 @@ private void sendPendingStreaming(@Nullable String cause) { intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready"); } try { - statusReceiver.sendIntent(mContext, 0, intent, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + statusReceiver.sendIntent(mContext, 0, intent, null /* onFinished*/, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (IntentSender.SendIntentException ignored) { } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9d3cb7012590..4c3f93510829 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5127,7 +5127,10 @@ public void freeStorage(final String volumeUuid, final long freeStorageSize, } if (pi != null) { try { - pi.sendIntent(null, success ? 1 : 0, null, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + pi.sendIntent(null, success ? 1 : 0, null /* intent */, null /* onFinished*/, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (SendIntentException e) { Slog.w(TAG, e); } @@ -13420,7 +13423,10 @@ static void onRestoreComplete(int returnCode, Context context, IntentSender targ fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageManager.installStatusToPublicStatus(returnCode)); try { - target.sendIntent(context, 0, fillIn, null, null); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); + target.sendIntent(context, 0, fillIn, null /* onFinished*/, + null /* handler */, null /* requiredPermission */, options.toBundle()); } catch (SendIntentException ignored) { } } From 4eddf0483676627129ec0a032204c89ccc0e5905 Mon Sep 17 00:00:00 2001 From: Justin Dunlap Date: Wed, 1 Mar 2023 00:08:22 +0000 Subject: [PATCH 130/208] Revert "Make Activites touch opaque - DO NOT MERGE" This reverts commit 74ce78dfb4179cb317d6e2fc3cabe5f60af5d02d. Reason for revert: re-release due to functional regression Change-Id: I71e2e889d1f2d026f708bdb617b73a5c7c10467d (cherry picked from commit dd4ee68792b9f8683637f57af10544a22c8f5c27) Merged-In: I71e2e889d1f2d026f708bdb617b73a5c7c10467d --- .../com/android/server/wm/ActivityRecord.java | 13 -- .../server/wm/ActivityRecordInputSink.java | 113 ------------------ 2 files changed, 126 deletions(-) delete mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e5a069847f5c..92dbfa4f1733 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -673,13 +673,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; - private final ActivityRecordInputSink mActivityRecordInputSink; - - // Activities with this uid are allowed to not create an input sink while being in the same - // task and directly above this ActivityRecord. This field is updated whenever a new activity - // is launched from this ActivityRecord. Touches are always allowed within the same uid. - int mAllowedTouchUid; - private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1657,8 +1650,6 @@ boolean isResolverOrChildActivity() { ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; mHandoverLaunchDisplayId = options.getLaunchDisplayId(); } - - mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3182,7 +3173,6 @@ private void cleanUpActivityServices() { @Override void removeImmediately() { onRemovedFromDisplay(); - mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6058,9 +6048,6 @@ void prepareSurfaces() { } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } - if (show) { - mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); - } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java deleted file mode 100644 index 95b5cec9a144..000000000000 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import android.os.Process; -import android.view.InputWindowHandle; -import android.view.SurfaceControl; -import android.view.WindowManager; - -/** - * Creates a InputWindowHandle that catches all touches that would otherwise pass through an - * Activity. - */ -class ActivityRecordInputSink { - - private final ActivityRecord mActivityRecord; - private final String mName; - - private InputWindowHandle mInputWindowHandle; - private SurfaceControl mSurfaceControl; - - ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { - mActivityRecord = activityRecord; - mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " - + mActivityRecord.mActivityComponent.flattenToShortString(); - if (sourceRecord != null) { - sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); - } - } - - public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { - boolean windowHandleChanged = updateInputWindowHandle(); - if (mSurfaceControl == null) { - mSurfaceControl = createSurface(transaction); - } - if (windowHandleChanged) { - transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); - } - } - - private SurfaceControl createSurface(SurfaceControl.Transaction t) { - SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) - .setName(mName) - .setHidden(false) - .setCallsite("ActivityRecordInputSink.createSurface") - .build(); - // Put layer below all siblings (and the parent surface too) - t.setLayer(surfaceControl, Integer.MIN_VALUE); - return surfaceControl; - } - - private boolean updateInputWindowHandle() { - boolean changed = false; - if (mInputWindowHandle == null) { - mInputWindowHandle = createInputWindowHandle(); - changed = true; - } - // Don't block touches from passing through to an activity below us in the same task, if - // that activity is either from the same uid or if that activity has launched an activity - // in our uid. - final ActivityRecord activityBelowInTask = - mActivityRecord.getTask().getActivityBelow(mActivityRecord); - final boolean allowPassthrough = activityBelowInTask != null && ( - activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() - || activityBelowInTask.isUid(mActivityRecord.getUid())); - boolean notTouchable = (mInputWindowHandle.layoutParamsFlags - & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; - if (allowPassthrough || mActivityRecord.isAppTransitioning()) { - mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - changed |= !notTouchable; - } else { - mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - changed |= notTouchable; - } - return changed; - } - - private InputWindowHandle createInputWindowHandle() { - InputWindowHandle inputWindowHandle = new InputWindowHandle(null, - mActivityRecord.getDisplayId()); - inputWindowHandle.replaceTouchableRegionWithCrop = true; - inputWindowHandle.name = mName; - inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; - inputWindowHandle.ownerUid = Process.myUid(); - inputWindowHandle.ownerPid = Process.myPid(); - inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - inputWindowHandle.inputFeatures = - WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; - return inputWindowHandle; - } - - void releaseSurfaceControl() { - if (mSurfaceControl != null) { - mSurfaceControl.release(); - mSurfaceControl = null; - } - } - -} From 4fffca91207a344c61645a0761856a2c9050e126 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Thu, 26 Jan 2023 17:43:24 -0800 Subject: [PATCH 131/208] [RESTRICT AUTOMERGE][pm] prevent system app downgrades of versions lower than preload Also remove misleading commandline output. BUG: 256202273 Test: manual 1. Install preload system app v90, reboot 2. (W/O data, W/ Flag, 90->80 NOK) adb install -d ~/Downloads/PrivApplication_80.apk Performing Streamed Install adb: failed to install /usr/local/google/home/schfan/Downloads/PrivApplication_80.apk: Failure [INSTALL_FAILED_VERSION_DOWNGRADE: System app: com.example.privapplication cannot be downgraded to older than its preloaded version on the system image. Update version code 80 is older than current 90] 3. (90->100) Install data app v100 4. (W/ data, W/O Flag, 100->90 NOK) adb install ~/Downloads/PrivApplication_90.apk Performing Streamed Install adb: failed to install /usr/local/google/home/schfan/Downloads/PrivApplication_90.apk: Failure [INSTALL_FAILED_VERSION_DOWNGRADE: Downgrade detected: Update version code 90 is older than current 100] 5. (W/ data, W/ Flag, 100->90 downgrade OK) adb install -d ~/Downloads/PrivApplication_90.apk Performing Streamed Install Success 6. (90->100) Install v100 6. (W/data, W/ Flag, 100->80 NOK) adb install -d ~/Downloads/PrivApplication_80.apk Performing Streamed Install adb: failed to install /usr/local/google/home/schfan/Downloads/PrivApplication_80.apk: Failure [INSTALL_FAILED_VERSION_DOWNGRADE: System app: com.example.privapplication cannot be downgraded to older than its preloaded version on the system image. Update version code 80 is older than current 90] Change-Id: I5a8ee9e29a3a58f6e3fd188e0122355744b8b0ce (cherry picked from commit a4484d7f1be1fa413258fe18644d61f85611f586) (cherry picked from commit on googleplex-android-review.googlesource.com host: aec76152d65cfd5774f6c0dcf4cb6009ba48c1ee) Merged-In: I5a8ee9e29a3a58f6e3fd188e0122355744b8b0ce --- .../server/pm/PackageManagerService.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4c3f93510829..1f89f3739e28 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -15077,10 +15077,10 @@ private int installLocationPolicy(PackageInfoLite pkgLite) { // will be null whereas dataOwnerPkg will contain information about the package // which was uninstalled while keeping its data. AndroidPackage dataOwnerPkg = installedPkg; + PackageSetting dataOwnerPs = mSettings.mPackages.get(packageName); if (dataOwnerPkg == null) { - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps != null) { - dataOwnerPkg = ps.pkg; + if (dataOwnerPs != null) { + dataOwnerPkg = dataOwnerPs.pkg; } } @@ -15104,12 +15104,32 @@ private int installLocationPolicy(PackageInfoLite pkgLite) { if (dataOwnerPkg != null) { if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, dataOwnerPkg.isDebuggable())) { + // Downgrade is not permitted; a lower version of the app will not be + // allowed try { checkDowngrade(dataOwnerPkg, pkgLite); } catch (PackageManagerException e) { Slog.w(TAG, "Downgrade detected: " + e.getMessage()); return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; } + } else if (dataOwnerPs.isSystem()) { + // Downgrade is permitted, but system apps can't be downgraded below + // the version preloaded onto the system image + final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr( + dataOwnerPs); + if (disabledPs != null) { + dataOwnerPkg = disabledPs.pkg; + } + try { + checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + String errorMsg = "System app: " + packageName + + " cannot be downgraded to" + + " older than its preloaded version on the system image. " + + e.getMessage(); + Slog.w(TAG, errorMsg); + return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; + } } } From 6467f0aee18838622accdc722e809719e39f2342 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Thu, 2 Feb 2023 10:35:56 -0800 Subject: [PATCH 132/208] [RESTRICT AUTOMERGE][pm] still allow debuggable for system app downgrades Turns out we do have internal tests that downgrades system apps, so adding this exception to allow for that. BUG: 267232653 BUG: 256202273 Test: manual Change-Id: Ie281bbdc8788ee64ff99a7c5150da7ce7926235e (cherry picked from commit ceeca68b8c3f0ed8427b0212f63defe2f075146e) (cherry picked from commit on googleplex-android-review.googlesource.com host: 341669af524058dd4c64a176ddc54ada589591e1) Merged-In: Ie281bbdc8788ee64ff99a7c5150da7ce7926235e --- .../server/pm/PackageManagerService.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1f89f3739e28..ca5183567ae7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -15120,15 +15120,19 @@ private int installLocationPolicy(PackageInfoLite pkgLite) { if (disabledPs != null) { dataOwnerPkg = disabledPs.pkg; } - try { - checkDowngrade(dataOwnerPkg, pkgLite); - } catch (PackageManagerException e) { - String errorMsg = "System app: " + packageName - + " cannot be downgraded to" - + " older than its preloaded version on the system image. " - + e.getMessage(); - Slog.w(TAG, errorMsg); - return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; + if (!Build.IS_DEBUGGABLE && !dataOwnerPkg.isDebuggable()) { + // Only restrict non-debuggable builds and non-debuggable version of + // the app + try { + checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + String errorMsg = "System app: " + packageName + + " cannot be downgraded to" + + " older than its preloaded version on the system image. " + + e.getMessage(); + Slog.w(TAG, errorMsg); + return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; + } } } } From 7448dda882d9570424121b8ec4b57fecd195652a Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Thu, 9 Feb 2023 12:28:26 -0800 Subject: [PATCH 133/208] Checks if AccessibilityServiceInfo is within parcelable size. - If too large when parsing service XMLs then skip this service. - If too large when a service attempts to update its own info then throw an error. Bug: 261589597 Test: atest AccessibilityServiceInfoTest Change-Id: Iffc0cd48cc713f7904d68059e141cb7de5a4b906 Merged-In: Iffc0cd48cc713f7904d68059e141cb7de5a4b906 (cherry picked from commit on googleplex-android-review.googlesource.com host: 553232c29079fbeab28f95307d025c1426aa7142) Merged-In: Iffc0cd48cc713f7904d68059e141cb7de5a4b906 --- .../accessibilityservice/AccessibilityService.java | 4 ++++ .../accessibilityservice/AccessibilityServiceInfo.java | 10 ++++++++++ .../accessibility/AccessibilityManagerService.java | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index ac00a042b79e..e52b9c177dc1 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -1982,6 +1982,10 @@ private void sendServiceInfo() { IAccessibilityServiceConnection connection = AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); if (mInfo != null && connection != null) { + if (!mInfo.isWithinParcelableSize()) { + throw new IllegalStateException( + "Cannot update service info: size is larger than safe parcelable limits."); + } try { connection.setServiceInfo(mInfo); mInfo = null; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index a41fa6431d4d..f853dff01e8a 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -39,6 +39,7 @@ import android.graphics.drawable.Drawable; import android.hardware.fingerprint.FingerprintManager; import android.os.Build; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; @@ -1028,6 +1029,15 @@ public int describeContents() { return 0; } + /** @hide */ + public final boolean isWithinParcelableSize() { + final Parcel parcel = Parcel.obtain(); + writeToParcel(parcel, 0); + final boolean result = parcel.dataSize() <= IBinder.MAX_IPC_SIZE; + parcel.recycle(); + return result; + } + public void writeToParcel(Parcel parcel, int flagz) { parcel.writeInt(eventTypes); parcel.writeStringArray(packageNames); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 03a44131bdd2..f9fbda357075 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1353,6 +1353,12 @@ private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState u AccessibilityServiceInfo accessibilityServiceInfo; try { accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext); + if (!accessibilityServiceInfo.isWithinParcelableSize()) { + Slog.e(LOG_TAG, "Skipping service " + + accessibilityServiceInfo.getResolveInfo().getComponentInfo() + + " because service info size is larger than safe parcelable limits."); + continue; + } if (userState.mCrashedServices.contains(serviceInfo.getComponentName())) { // Restore the crashed attribute. accessibilityServiceInfo.crashed = true; From 0d3f38030433e04e922bd48476cd3b6e45f46508 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 7 Apr 2022 21:42:04 +0100 Subject: [PATCH 134/208] Uri: check authority and scheme as part of determining URI path The interpretation of the path depends on whether the scheme or authority are specified and should be observed when unparcelling URIs. Bug: 171966843 Test: atest FrameworksCoreTests:android.net.UriTest Test: atest com.android.devicehealthchecks.SystemAppCheck Change-Id: I06981d1c6e387b16df792494523994518848db37 Merged-In: I06981d1c6e387b16df792494523994518848db37 (cherry picked from commit f37a94ae920fa5879c557603fc285942ec4b84b1) (cherry picked from commit on googleplex-android-review.googlesource.com host: c87f0623be4042c39a9b73f7a6e02aa116925e50) Merged-In: I06981d1c6e387b16df792494523994518848db37 --- core/java/android/net/Uri.java | 22 +++++--- .../coretests/src/android/net/UriTest.java | 54 +++++++++++++++++++ 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 1cb4fe8cf4e7..84fc4f78edc4 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1194,13 +1194,16 @@ private HierarchicalUri(String scheme, Part authority, PathPart path, } static Uri readFrom(Parcel parcel) { - return new HierarchicalUri( - parcel.readString8(), - Part.readFrom(parcel), - PathPart.readFrom(parcel), - Part.readFrom(parcel), - Part.readFrom(parcel) - ); + final String scheme = parcel.readString8(); + final Part authority = Part.readFrom(parcel); + // In RFC3986 the path should be determined based on whether there is a scheme or + // authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3). + final boolean hasSchemeOrAuthority = + (scheme != null && scheme.length() > 0) || !authority.isEmpty(); + final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel); + final Part query = Part.readFrom(parcel); + final Part fragment = Part.readFrom(parcel); + return new HierarchicalUri(scheme, authority, path, query, fragment); } public int describeContents() { @@ -2259,6 +2262,11 @@ static PathPart readFrom(Parcel parcel) { } } + static PathPart readFrom(boolean hasSchemeOrAuthority, Parcel parcel) { + final PathPart path = readFrom(parcel); + return hasSchemeOrAuthority ? makeAbsolute(path) : path; + } + /** * Creates a path from the encoded string. * diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index f20220c4ab9b..670aefd21d36 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -48,6 +48,7 @@ public void testToStringWithPathOnly() { public void testParcelling() { parcelAndUnparcel(Uri.parse("foo:bob%20lee")); parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment")); + parcelAndUnparcel(Uri.fromParts("https", "www.google.com", null)); parcelAndUnparcel(new Uri.Builder() .scheme("http") .authority("crazybob.org") @@ -873,9 +874,62 @@ private static void assertUnparcelLegacyPart_fails(Class partClass) throws Excep Throwable targetException = expected.getTargetException(); // Check that the exception was thrown for the correct reason. assertEquals("Unknown representation: 0", targetException.getMessage()); + } finally { + parcel.recycle(); } } + private Uri buildUriFromRawParcel(boolean argumentsEncoded, + String scheme, + String authority, + String path, + String query, + String fragment) { + // Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}). + final int representation = argumentsEncoded ? 1 : 2; + Parcel parcel = Parcel.obtain(); + try { + parcel.writeInt(3); // hierarchical + parcel.writeString8(scheme); + parcel.writeInt(representation); + parcel.writeString8(authority); + parcel.writeInt(representation); + parcel.writeString8(path); + parcel.writeInt(representation); + parcel.writeString8(query); + parcel.writeInt(representation); + parcel.writeString8(fragment); + parcel.setDataPosition(0); + return Uri.CREATOR.createFromParcel(parcel); + } finally { + parcel.recycle(); + } + } + + public void testUnparcelMalformedPath() { + // Regression tests for b/171966843. + + // Test cases with arguments encoded (covering testing `scheme` * `authority` options). + Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null); + assertEquals("https://google.com/@evil.com", uri0.toString()); + Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x"); + assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString()); + Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null); + assertEquals("http::/@evil.com", uri2.toString()); + Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null); + assertEquals("@evil.com", uri3.toString()); + + // Test cases with arguments not encoded (covering testing `scheme` * `authority` options). + Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null); + assertEquals("https://google.com/%40evil.com", uriA.toString()); + Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null); + assertEquals("//google.com/%40evil.com", uriB.toString()); + Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null); + assertEquals("http::/%40evil.com", uriC.toString()); + Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y"); + assertEquals("%40evil.com?name%3Dspark#y", uriD.toString()); + } + public void testToSafeString() { checkToSafeString("tel:xxxxxx", "tel:Google"); checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890"); From a6e8b283781bfef67e73f10202e437f7e246711c Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Mon, 21 Nov 2022 17:38:21 -0800 Subject: [PATCH 135/208] enforce stricter rules when registering phoneAccounts - include disable accounts when looking up accounts for a package to check if the limit is reached (10) - put a new limit of 10 supported schemes - put a new limit of 256 characters per scheme - put a new limit of 256 characters per address - ensure the Icon can write to memory w/o throwing an exception bug: 259064622 bug: 256819769 Test: cts + unit Change-Id: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 Merged-In: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 (cherry picked from commit on googleplex-android-review.googlesource.com host: a66a3156e03fbd1c3a29015db9193d66f2709f98) Merged-In: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 --- .../java/android/telecom/PhoneAccount.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 768c8eebf067..a85c85380fb7 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -483,6 +483,11 @@ public Builder setLabel(CharSequence label) { /** * Sets the address. See {@link PhoneAccount#getAddress}. + *

+ * Note: The entire URI value is limited to 256 characters. This check is + * enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an + * {@link IllegalArgumentException} to be thrown if URI is over 256. * * @param value The address of the phone account. * @return The builder. @@ -516,6 +521,10 @@ public Builder setCapabilities(int value) { /** * Sets the icon. See {@link PhoneAccount#getIcon}. + *

+ * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory. + * This check is enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} * * @param icon The icon to set. */ @@ -549,6 +558,10 @@ public Builder setShortDescription(CharSequence value) { /** * Specifies an additional URI scheme supported by the {@link PhoneAccount}. * + *

+ * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will + * cause an {@link IllegalArgumentException} to be thrown when the account is registered. + * * @param uriScheme The URI scheme. * @return The builder. */ @@ -562,6 +575,12 @@ public Builder addSupportedUriScheme(String uriScheme) { /** * Specifies the URI schemes supported by the {@link PhoneAccount}. * + *

+ * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is + * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any + * scheme will cause an {@link IllegalArgumentException} to be thrown when the account + * is registered. + * * @param uriSchemes The URI schemes. * @return The builder. */ From 4e6c5bedbf634499d5ac0e4f45513424bf5d52bc Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Wed, 1 Mar 2023 11:20:16 +0100 Subject: [PATCH 136/208] Make Activites touch opaque - DO NOT MERGE Block touches from passing through activities by adding a dedicated surface that consumes all touches that would otherwise pass through the bounds availble to the Activity. + Keep displayId in sync for ActivityRecord Bug: 194480991 Test: atest CtsWindowManagerDeviceTestCases:ActivityRecordInputSinkTests Test: atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests Test: atest CtsWindowManagerDeviceTestCases:PinnedStackTests Test: Used "System > Developer Options > Simulate secondary display" to test that moving activites between displays work as intended. Change-Id: Idace4104c2708ce99309fa54fd44dd000e7f8894 (cherry picked from commit on googleplex-android-review.googlesource.com host: 73ee4cb9c4f108c2edf68443c339bc64a9240322) Merged-In: Idace4104c2708ce99309fa54fd44dd000e7f8894 --- .../com/android/server/wm/ActivityRecord.java | 13 ++ .../server/wm/ActivityRecordInputSink.java | 117 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 92dbfa4f1733..e5a069847f5c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -673,6 +673,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; + private final ActivityRecordInputSink mActivityRecordInputSink; + + // Activities with this uid are allowed to not create an input sink while being in the same + // task and directly above this ActivityRecord. This field is updated whenever a new activity + // is launched from this ActivityRecord. Touches are always allowed within the same uid. + int mAllowedTouchUid; + private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1650,6 +1657,8 @@ boolean isResolverOrChildActivity() { ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; mHandoverLaunchDisplayId = options.getLaunchDisplayId(); } + + mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3173,6 +3182,7 @@ private void cleanUpActivityServices() { @Override void removeImmediately() { onRemovedFromDisplay(); + mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6048,6 +6058,9 @@ void prepareSurfaces() { } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } + if (show) { + mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); + } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java new file mode 100644 index 000000000000..95a6e8b8b88f --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.os.Process; +import android.view.InputWindowHandle; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * Creates a InputWindowHandle that catches all touches that would otherwise pass through an + * Activity. + */ +class ActivityRecordInputSink { + + private final ActivityRecord mActivityRecord; + private final String mName; + + private InputWindowHandle mInputWindowHandle; + private SurfaceControl mSurfaceControl; + + ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { + mActivityRecord = activityRecord; + mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " + + mActivityRecord.mActivityComponent.flattenToShortString(); + if (sourceRecord != null) { + sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); + } + } + + public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { + boolean windowHandleChanged = updateInputWindowHandle(); + if (mSurfaceControl == null) { + mSurfaceControl = createSurface(transaction); + } + if (windowHandleChanged) { + transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); + } + } + + private SurfaceControl createSurface(SurfaceControl.Transaction t) { + SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) + .setName(mName) + .setHidden(false) + .setCallsite("ActivityRecordInputSink.createSurface") + .build(); + // Put layer below all siblings (and the parent surface too) + t.setLayer(surfaceControl, Integer.MIN_VALUE); + return surfaceControl; + } + + private boolean updateInputWindowHandle() { + boolean changed = false; + if (mInputWindowHandle == null) { + mInputWindowHandle = createInputWindowHandle(); + changed = true; + } + // Don't block touches from passing through to an activity below us in the same task, if + // that activity is either from the same uid or if that activity has launched an activity + // in our uid. + final ActivityRecord activityBelowInTask = + mActivityRecord.getTask().getActivityBelow(mActivityRecord); + final boolean allowPassthrough = activityBelowInTask != null && ( + activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() + || activityBelowInTask.isUid(mActivityRecord.getUid())); + boolean notTouchable = (mInputWindowHandle.layoutParamsFlags + & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; + if (allowPassthrough || mActivityRecord.isAppTransitioning()) { + mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= !notTouchable; + } else { + mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= notTouchable; + } + if (mInputWindowHandle.displayId != mActivityRecord.getDisplayId()) { + mInputWindowHandle.displayId = mActivityRecord.getDisplayId(); + changed = true; + } + return changed; + } + + private InputWindowHandle createInputWindowHandle() { + InputWindowHandle inputWindowHandle = new InputWindowHandle(null, + mActivityRecord.getDisplayId()); + inputWindowHandle.replaceTouchableRegionWithCrop = true; + inputWindowHandle.name = mName; + inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; + inputWindowHandle.ownerUid = Process.myUid(); + inputWindowHandle.ownerPid = Process.myPid(); + inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + inputWindowHandle.inputFeatures = + WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; + return inputWindowHandle; + } + + void releaseSurfaceControl() { + if (mSurfaceControl != null) { + mSurfaceControl.release(); + mSurfaceControl = null; + } + } + +} From 88360b63675b739345fe830ddda1da9e9595f085 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: [PATCH 137/208] Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Bug: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a Merged-In: If7c7db6694330ffbac551d044efadb26219fe17f (cherry picked from commit on googleplex-android-review.googlesource.com host: 931093dfb41fc41659c9f2d6f76bd74e85cf1da8) Merged-In: If7c7db6694330ffbac551d044efadb26219fe17f --- .../server/notification/SnoozeHelper.java | 29 +++++++++---- .../server/notification/SnoozeHelperTest.java | 41 ++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index a29ac131f94c..a6cae49c4fd6 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -62,6 +62,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -150,7 +153,7 @@ protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, Stri ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -164,7 +167,7 @@ protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -249,7 +252,8 @@ protected void snooze(NotificationRecord record, long duration) { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -260,8 +264,10 @@ protected void snooze(NotificationRecord record, String contextId) { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -278,6 +284,13 @@ private void snooze(NotificationRecord record) { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -382,12 +395,14 @@ protected void repost(String key, boolean muteOnReturn) { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 83ba7108af7f..2b6fbea5130a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -67,6 +67,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -245,6 +246,37 @@ public void testScheduleRepostsForPersistedNotifications() throws Exception { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = String.join("", Collections.nCopies(66000, "A")); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + XmlSerializer serializer = Xml.newSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -593,13 +625,20 @@ public void repostGroupSummary_repostsSummary() throws Exception { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, + "two" + String.join("", Collections.nCopies(66000, "2")), UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); From 1c01054aea342fbc6a0e4be9d352f5f81c9d1808 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 15 Feb 2023 15:20:25 -0800 Subject: [PATCH 138/208] Limit the number of shortcuts per app that can be retained by system This is a second attempt at fixing the issue, the previous CL ag/20642213 was reverted because it simply throws an exception when the limit is reached, which causes apps to crash since chat apps tends to be sending large amount of conversation shortcuts and they have no way to know how many of these shortcuts are still cached by the system. Instead of throwing an exception, this CL simply removes excessive shortcuts to avoid crashes. Currently there is a limit on the number of shortcuts an app can publish in respect to each launcher activity. This CL further implements a global maximum of total number of shortcuts that can be retained for an app to mitigate from any potential system health issue. When the global maximum is reached, ShortcutService will proactively removes shortcuts from system memory. Cached shortcuts are removed first, followed by dynamic shortcuts, using last updated time as tie-breaker. This CL additionally addresses an unexpected flow where re-publishing previously removed shortcuts that are still retained by the system could cause the total number of shortcuts to exceed previously set limit. Bug: 250576066 233155034 Test: manual Change-Id: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 Merged-In: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 (cherry picked from commit on googleplex-android-review.googlesource.com host: 2296ccfde0678b86f22e1da7bd57518f3bfafbba) Merged-In: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 --- .../android/server/pm/ShortcutPackage.java | 79 ++++++++++++++++++- .../android/server/pm/ShortcutService.java | 25 +++++- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index f2bfb2aa1405..b2d78ec7d95d 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -68,6 +68,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Package information used by {@link ShortcutService}. @@ -350,6 +351,7 @@ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut, @NonNull List changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); + ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); @@ -357,7 +359,7 @@ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut, final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); boolean deleted = false; - if (oldShortcut == null) { + if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); @@ -367,7 +369,6 @@ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut, if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. - // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); @@ -382,7 +383,8 @@ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut, changedShortcuts.add(shortcut); deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; } - } else { + } + if (oldShortcut != null) { // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); @@ -396,6 +398,30 @@ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut, return deleted; } + private void ensureShortcutCountBeforePush() { + final ShortcutService service = mShortcutUser.mService; + // Ensure the total number of shortcuts doesn't exceed the hard limit per app. + final int maxShortcutPerApp = service.getMaxAppShortcuts(); + final List appShortcuts = mShortcuts.values().stream().filter(si -> + !si.isPinned()).collect(Collectors.toList()); + if (appShortcuts.size() >= maxShortcutPerApp) { + // Max has reached. Removes shortcuts until they fall within the hard cap. + // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). + Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); + + while (appShortcuts.size() >= maxShortcutPerApp) { + final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); + if (shortcut.isDeclaredInManifest()) { + // All shortcuts are manifest shortcuts and cannot be removed. + throw new IllegalArgumentException(getPackageName() + " has published " + + appShortcuts.size() + " manifest shortcuts across different" + + " activities."); + } + forceDeleteShortcutInner(shortcut.getId()); + } + } + } + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * @@ -1222,6 +1248,53 @@ private boolean pushOutExcessShortcuts() { return Integer.compare(a.getRank(), b.getRank()); }; + /** + * To sort by isManifestShortcut(), isDynamic(), getRank() and + * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, + * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. + * + * This is used to decide which shortcuts to remove when the total number of shortcuts retained + * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. + * + * (Note the number of manifest shortcuts is always <= the max number, because if there are + * more, ShortcutParser would ignore the rest.) + */ + final Comparator mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, + ShortcutInfo b) -> { + if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { + return -1; + } + if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { + return 1; + } + if (a.isDynamic() && b.isDynamic()) { + return Integer.compare(a.getRank(), b.getRank()); + } + if (a.isDynamic()) { + return -1; + } + if (b.isDynamic()) { + return 1; + } + if (a.isCached() && b.isCached()) { + // if both shortcuts are cached, prioritize shortcuts cached by bubbles. + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return 1; + } + } + if (a.isCached()) { + return -1; + } + if (b.isCached()) { + return 1; + } + return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); + }; + /** * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 9903c2d993cc..4e73bd2cec40 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -171,6 +171,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; + @VisibleForTesting + static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; + @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; @@ -245,6 +248,11 @@ interface ConfigConstants { */ String KEY_MAX_SHORTCUTS = "max_shortcuts"; + /** + * Key name for the max shortcuts can be retained in system ram per app. (int) + */ + String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; + /** * Key name for icon compression quality, 0-100. */ @@ -302,10 +310,15 @@ public boolean test(PackageInfo pi) { new SparseArray<>(); /** - * Max number of dynamic + manifest shortcuts that each application can have at a time. + * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ private int mMaxShortcuts; + /** + * Max number of shortcuts that can exists in system ram for each application. + */ + private int mMaxShortcutsPerApp; + /** * Max number of updating API calls that each application can make during the interval. */ @@ -732,6 +745,9 @@ boolean updateConfigurationLocked(String config) { mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); + mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong( + ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP)); + final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, @@ -1661,6 +1677,13 @@ int getMaxActivityShortcuts() { return mMaxShortcuts; } + /** + * Return the max number of shortcuts can be retaiend in system ram for each application. + */ + int getMaxAppShortcuts() { + return mMaxShortcutsPerApp; + } + /** * - Sends a notification to LauncherApps * - Write to file From de67ebfb8ac3ca1579edc58323eddd6b2811bd1c Mon Sep 17 00:00:00 2001 From: arthurhung Date: Thu, 23 Jul 2020 17:15:04 +0800 Subject: [PATCH 139/208] Fix WindowInputTests#testOverlapWindow failing (1/2) The 'InputWindowInfo.isTrustedOverlay' function had been removed, we have to indicate the trustedOverlay flag when fill input info to overlay window. Bug: 161789401 Bug: 158717144 Test: atest WindowInputTests Change-Id: Idf17fd64c6a1399d04c51e3f0bd600dc4a84239a Merged-In: Idf17fd64c6a1399d04c51e3f0bd600dc4a84239a (cherry picked from commit on googleplex-android-review.googlesource.com host: 657c2a411593ffb95c7f91a972bbe84f1a2df790) Merged-In: Idf17fd64c6a1399d04c51e3f0bd600dc4a84239a --- .../android/server/wm/InputConsumerImpl.java | 1 + .../com/android/server/wm/InputMonitor.java | 32 +++++++++++++++---- .../com/android/server/wm/WindowState.java | 14 +------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 19185736fc89..6bbaa8f27f2d 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -86,6 +86,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mWindowHandle.ownerUid = Process.myUid(); mWindowHandle.inputFeatures = 0; mWindowHandle.scaleFactor = 1.0f; + mWindowHandle.trustedOverlay = true; mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession()) .setContainerLayer() diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 8aec9ed08880..80e16edfa2e8 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -27,7 +27,18 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -66,9 +77,6 @@ final class InputMonitor { private boolean mUpdateInputWindowsPending; private boolean mUpdateInputWindowsImmediately; - // Currently focused input window handle. - private InputWindowHandle mFocusedInputWindowHandle; - private boolean mDisableWallpaperTouchEvents; private final Rect mTmpRect = new Rect(); private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer; @@ -320,10 +328,6 @@ void populateInputWindowHandle(final InputWindowHandle inputWindowHandle, Slog.d(TAG_WM, "addInputWindowHandle: " + child + ", " + inputWindowHandle); } - - if (hasFocus) { - mFocusedInputWindowHandle = inputWindowHandle; - } } void setUpdateInputWindowsNeededLw() { @@ -580,6 +584,7 @@ private static void populateOverlayInputInfo(final InputWindowHandle inputWindow inputWindowHandle.portalToDisplayId = INVALID_DISPLAY; inputWindowHandle.touchableRegion.setEmpty(); inputWindowHandle.setTouchableRegionCrop(null); + inputWindowHandle.trustedOverlay = isTrustedOverlay(type); } /** @@ -594,4 +599,17 @@ static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transac populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true); t.setInputWindowInfo(sc, inputWindowHandle); } + + static boolean isTrustedOverlay(int type) { + return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY + || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG + || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR + || type == TYPE_NOTIFICATION_SHADE + || type == TYPE_NAVIGATION_BAR + || type == TYPE_NAVIGATION_BAR_PANEL + || type == TYPE_SECURE_SYSTEM_OVERLAY + || type == TYPE_DOCK_DIVIDER + || type == TYPE_ACCESSIBILITY_OVERLAY + || type == TYPE_INPUT_CONSUMER; + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 74b5840512d2..7e0acab1faa1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -62,7 +62,6 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; @@ -87,7 +86,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; @@ -956,17 +954,7 @@ public boolean isInteractive() { mInputWindowHandle.trustedOverlay = (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 && mOwnerCanAddInternalSystemWindow; - mInputWindowHandle.trustedOverlay |= - mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY - || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG - || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR - || mAttrs.type == TYPE_NOTIFICATION_SHADE - || mAttrs.type == TYPE_NAVIGATION_BAR - || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL - || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY - || mAttrs.type == TYPE_DOCK_DIVIDER - || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY - || mAttrs.type == TYPE_INPUT_CONSUMER; + mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type); // Make sure we initial all fields before adding to parentWindow, to prevent exception // during onDisplayChanged. From 1c7a67748e735821f595ffeaab7710066189fedc Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Wed, 17 Aug 2022 09:37:18 -0700 Subject: [PATCH 140/208] [RESTRICT AUTOMERGE] [SettingsProvider] mem limit should be checked before settings are updated Previously, a setting is updated before the memory usage limit check, which can be exploited by malicious apps and cause OoM DoS. This CL changes the logic to checkMemLimit -> update -> updateMemUsage. BUG: 239415861 Test: atest com.android.providers.settings.SettingsStateTest (cherry picked from commit 8eeb92950f4a7012d4cf282106a1418fd211f475) Merged-In: I20551a2dba9aa79efa0c064824f349f551c2c2e4 Change-Id: I20551a2dba9aa79efa0c064824f349f551c2c2e4 (cherry picked from commit f22267f8b2f92b68bf3b9d92d6ec7cff7ed0f93d) Merged-In: I20551a2dba9aa79efa0c064824f349f551c2c2e4 --- .../providers/settings/SettingsState.java | 75 ++++++++++++------- .../providers/settings/SettingsStateTest.java | 38 ++++++++++ 2 files changed, 86 insertions(+), 27 deletions(-) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 6678cf6f1033..f7ca8b039633 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -384,9 +384,11 @@ public void resetSettingDefaultValueLocked(String name) { Setting newSetting = new Setting(name, oldSetting.getValue(), null, oldSetting.getPackageName(), oldSetting.getTag(), false, oldSetting.getId()); - mSettings.put(name, newSetting); - updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, + int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); + checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); + mSettings.put(name, newSetting); + updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); scheduleWriteIfNeededLocked(); } } @@ -419,6 +421,12 @@ public boolean insertSettingLocked(String name, String value, String tag, Setting oldState = mSettings.get(name); String oldValue = (oldState != null) ? oldState.value : null; String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; + String newDefaultValue = makeDefault ? value : oldDefaultValue; + + int newSize = getNewMemoryUsagePerPackageLocked(packageName, oldValue, value, + oldDefaultValue, newDefaultValue); + checkNewMemoryUsagePerPackageLocked(packageName, newSize); + Setting newState; if (oldState != null) { @@ -439,8 +447,7 @@ oldValue, tag, makeDefault, getUserIdFromKey(mKey), addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); - updateMemoryUsagePerPackageLocked(packageName, oldValue, value, - oldDefaultValue, newState.getDefaultValue()); + updateMemoryUsagePerPackageLocked(packageName, newSize); scheduleWriteIfNeededLocked(); @@ -558,13 +565,14 @@ public boolean deleteSettingLocked(String name) { } Setting oldState = mSettings.remove(name); + int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, + null, oldState.defaultValue, null); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); - updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, - null, oldState.defaultValue, null); + updateMemoryUsagePerPackageLocked(oldState.packageName, newSize); addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); @@ -585,16 +593,18 @@ public boolean resetSettingLocked(String name) { Setting oldSetting = new Setting(setting); String oldValue = setting.getValue(); String oldDefaultValue = setting.getDefaultValue(); + String newValue = oldDefaultValue; + String newDefaultValue = oldDefaultValue; + + int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, oldValue, + newValue, oldDefaultValue, newDefaultValue); + checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); if (!setting.reset()) { return false; } - String newValue = setting.getValue(); - String newDefaultValue = setting.getDefaultValue(); - - updateMemoryUsagePerPackageLocked(setting.packageName, oldValue, - newValue, oldDefaultValue, newDefaultValue); + updateMemoryUsagePerPackageLocked(setting.packageName, newSize); addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); @@ -702,38 +712,49 @@ public void dumpHistoricalOperations(PrintWriter pw) { } @GuardedBy("mLock") - private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, - String newValue, String oldDefaultValue, String newDefaultValue) { - if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { - return; - } + private boolean isExemptFromMemoryUsageCap(String packageName) { + return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED + || SYSTEM_PACKAGE_NAME.equals(packageName); + } - if (SYSTEM_PACKAGE_NAME.equals(packageName)) { + @GuardedBy("mLock") + private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize) + throws IllegalStateException { + if (isExemptFromMemoryUsageCap(packageName)) { return; } + if (newSize > mMaxBytesPerAppPackage) { + throw new IllegalStateException("You are adding too many system settings. " + + "You should stop using system settings for app specific data" + + " package: " + packageName); + } + } + @GuardedBy("mLock") + private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValue, + String newValue, String oldDefaultValue, String newDefaultValue) { + if (isExemptFromMemoryUsageCap(packageName)) { + return 0; + } + final Integer currentSize = mPackageToMemoryUsage.get(packageName); final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; final int newValueSize = (newValue != null) ? newValue.length() : 0; final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; final int deltaSize = newValueSize + newDefaultValueSize - oldValueSize - oldDefaultValueSize; + return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0); + } - Integer currentSize = mPackageToMemoryUsage.get(packageName); - final int newSize = Math.max((currentSize != null) - ? currentSize + deltaSize : deltaSize, 0); - - if (newSize > mMaxBytesPerAppPackage) { - throw new IllegalStateException("You are adding too many system settings. " - + "You should stop using system settings for app specific data" - + " package: " + packageName); + @GuardedBy("mLock") + private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) { + if (isExemptFromMemoryUsageCap(packageName)) { + return; } - if (DEBUG) { Slog.i(LOG_TAG, "Settings for package: " + packageName + " size: " + newSize + " bytes."); } - mPackageToMemoryUsage.put(packageName, newSize); } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 9f448af7f344..bf000cd8a22a 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -21,6 +21,8 @@ import org.xmlpull.v1.XmlSerializer; +import com.google.common.base.Strings; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -280,4 +282,40 @@ private SettingsState getSettingStateObject() { settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); return settingsState; } + + public void testInsertSetting_memoryUsage() { + SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + // No exception should be thrown when there is no cap + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + settingsState.deleteSettingLocked(SETTING_NAME); + + settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); + // System package doesn't have memory usage limit + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, SYSTEM_PACKAGE); + settingsState.deleteSettingLocked(SETTING_NAME); + + // Should not throw if usage is under the cap + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19999), + null, false, "p1"); + settingsState.deleteSettingLocked(SETTING_NAME); + try { + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("p1")); + } + try { + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("p1")); + } + assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull()); + } } From 104b0fc5768d6aa6325c5308656aa43a37209c41 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Tue, 11 Oct 2022 18:08:11 -0700 Subject: [PATCH 141/208] [RESTRICT AUTOMERGE][SettingsProvider] key size limit for mutating settings Prior to targetSdk 22, apps could add random system settings keys which opens an opportunity for OOM attacks. This CL adds a key size limit. BUG: 239415997 Test: manual; will add cts test Merged-In: Ic9e88c0cc3d7206c64ba5b5c7d15b50d1ffc9adc Change-Id: Ic9e88c0cc3d7206c64ba5b5c7d15b50d1ffc9adc (cherry picked from commit 783bcba343c480f6ccedaaff41ba7171a1082e0c) (cherry picked from commit 15f6e85ab4ee8997d80ecd3d16bb60012ee493a1) Merged-In: Ic9e88c0cc3d7206c64ba5b5c7d15b50d1ffc9adc --- .../providers/settings/SettingsState.java | 40 +++++--- .../providers/settings/SettingsStateTest.java | 94 ++++++++++++++++++- 2 files changed, 120 insertions(+), 14 deletions(-) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index f7ca8b039633..c252db11eaf2 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -47,6 +47,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -384,8 +385,8 @@ public void resetSettingDefaultValueLocked(String name) { Setting newSetting = new Setting(name, oldSetting.getValue(), null, oldSetting.getPackageName(), oldSetting.getTag(), false, oldSetting.getId()); - int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, - newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); + int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0, + oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); mSettings.put(name, newSetting); updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); @@ -423,8 +424,9 @@ public boolean insertSettingLocked(String name, String value, String tag, String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; String newDefaultValue = makeDefault ? value : oldDefaultValue; - int newSize = getNewMemoryUsagePerPackageLocked(packageName, oldValue, value, - oldDefaultValue, newDefaultValue); + int newSize = getNewMemoryUsagePerPackageLocked(packageName, + oldValue == null ? name.length() : 0 /* deltaKeySize */, + oldValue, value, oldDefaultValue, newDefaultValue); checkNewMemoryUsagePerPackageLocked(packageName, newSize); Setting newState; @@ -565,8 +567,12 @@ public boolean deleteSettingLocked(String name) { } Setting oldState = mSettings.remove(name); - int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, - null, oldState.defaultValue, null); + if (oldState == null) { + return false; + } + int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, + -name.length() /* deltaKeySize */, + oldState.value, null, oldState.defaultValue, null); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), @@ -589,15 +595,16 @@ public boolean resetSettingLocked(String name) { } Setting setting = mSettings.get(name); + if (setting == null) { + return false; + } Setting oldSetting = new Setting(setting); String oldValue = setting.getValue(); String oldDefaultValue = setting.getDefaultValue(); - String newValue = oldDefaultValue; - String newDefaultValue = oldDefaultValue; - int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, oldValue, - newValue, oldDefaultValue, newDefaultValue); + int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue, + oldDefaultValue, oldDefaultValue, oldDefaultValue); checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); if (!setting.reset()) { @@ -731,8 +738,8 @@ private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize } @GuardedBy("mLock") - private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValue, - String newValue, String oldDefaultValue, String newDefaultValue) { + private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize, + String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) { if (isExemptFromMemoryUsageCap(packageName)) { return 0; } @@ -741,7 +748,7 @@ private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValu final int newValueSize = (newValue != null) ? newValue.length() : 0; final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; - final int deltaSize = newValueSize + newDefaultValueSize + final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize - oldValueSize - oldDefaultValueSize; return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0); } @@ -1570,4 +1577,11 @@ public static boolean isSystemPackage(Context context, String packageName, int u return false; } } + + @VisibleForTesting + public int getMemoryUsage(String packageName) { + synchronized (mLock) { + return mPackageToMemoryUsage.getOrDefault(packageName, 0); + } + } } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index bf000cd8a22a..36b892def814 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -299,7 +299,7 @@ public void testInsertSetting_memoryUsage() { settingsState.deleteSettingLocked(SETTING_NAME); // Should not throw if usage is under the cap - settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19999), + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975), null, false, "p1"); settingsState.deleteSettingLocked(SETTING_NAME); try { @@ -317,5 +317,97 @@ public void testInsertSetting_memoryUsage() { assertTrue(ex.getMessage().contains("p1")); } assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull()); + try { + settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + } + + public void testMemoryUsagePerPackage() { + SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); + + // Test inserting one key with default + final String testKey1 = SETTING_NAME; + final String testValue1 = Strings.repeat("A", 100); + settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE); + int expectedMemUsage = testKey1.length() + testValue1.length() + + testValue1.length() /* size for default */; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test inserting another key + final String testKey2 = SETTING_NAME + "2"; + settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE); + expectedMemUsage += testKey2.length() + testValue1.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating first key with new default + final String testValue2 = Strings.repeat("A", 300); + settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE); + expectedMemUsage += (testValue2.length() - testValue1.length()) * 2; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating first key without new default + final String testValue3 = Strings.repeat("A", 50); + settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE); + expectedMemUsage -= testValue2.length() - testValue3.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating second key + settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE); + expectedMemUsage -= testValue1.length() - testValue2.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test resetting key + settingsState.resetSettingLocked(testKey1); + expectedMemUsage += testValue2.length() - testValue3.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test resetting default value + settingsState.resetSettingDefaultValueLocked(testKey1); + expectedMemUsage -= testValue2.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test deletion + settingsState.deleteSettingLocked(testKey2); + expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test another package with a different key + final String testPackage2 = TEST_PACKAGE + "2"; + final String testKey3 = SETTING_NAME + "3"; + settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2; + assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); + + // Test system package + settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); + assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE)); + + // Test invalid value + try { + settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false, + TEST_PACKAGE); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test invalid key + try { + settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false, + TEST_PACKAGE); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); } } From 6ddb6423021def552238af0217745f0fad296683 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 22 Feb 2023 15:14:08 +0000 Subject: [PATCH 142/208] Prevent sharesheet from previewing unowned URIs Bug: 261036568 Test: manually via supplied tool (see bug) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3062b80fb28014a7482d5fa8b2a5c852134a5845) Merged-In: I21accf6f753d2f676f1602d6e1ce829c5ef29e9a Change-Id: I21accf6f753d2f676f1602d6e1ce829c5ef29e9a --- .../android/internal/app/ChooserActivity.java | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 796a557f45af..8ac2ca49e9eb 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.content.ContentProvider.getUserIdFromUri; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; @@ -148,6 +150,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1300,7 +1303,7 @@ private ViewGroup displayTextContentPreview(Intent targetIntent, LayoutInflater ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1327,6 +1330,10 @@ private ViewGroup displayImageContentPreview(Intent targetIntent, LayoutInflater String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); } else { ContentResolver resolver = getContentResolver(); @@ -1334,7 +1341,7 @@ private ViewGroup displayImageContentPreview(Intent targetIntent, LayoutInflater List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); List imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1441,9 +1448,16 @@ private ViewGroup displayFileContentPreview(Intent targetIntent, LayoutInflater String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1497,6 +1511,24 @@ private void loadFileUriIntoView(final Uri uri, final View parent) { } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); From a832aad25f4eee8dba970fd5dc9c530f578b7f71 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 15 Feb 2023 20:39:44 +0100 Subject: [PATCH 143/208] [DO NOT MERGE] Wait for preloading images to complete before inflating notifications NotificationContentInflater waits on SysUiBg thread for images to load, with a timeout of 1000ms. Test: 1. Build a test app that posts MessagingStyle notifications with a huge image (8k+) set as data Uri. 2. SystemUi should not ANR 3. adb logcat | grep NotificationInlineImageCache - shows timeout/cancellation logs Bug: 252766417 Bug: 223859644 (cherry picked from commit 195043f40e46ddcd2fe534a9dac344792d39d91c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:733089e71ca4b98417586e593a1fb0e50f3a5c61) Merged-In: I341db60223214cf2282b5c0270e343e1ce95fa01 Change-Id: I341db60223214cf2282b5c0270e343e1ce95fa01 --- .../row/NotificationContentInflater.java | 16 +++++- .../row/NotificationInlineImageCache.java | 24 ++++++-- .../row/NotificationInlineImageResolver.java | 57 +++++++++++++++++-- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index a7d83b3b2774..acab13df0a6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -435,6 +435,7 @@ public RemoteViews getRemoteView() { CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); + return cancellationSignal; } @@ -700,6 +701,7 @@ public void setInflateSynchronously(boolean inflateSynchronously) { public static class AsyncInflationTask extends AsyncTask implements InflationCallback, InflationTask { + private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; @@ -786,10 +788,16 @@ protected InflationProgress doInBackground(Void... params) { InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); - return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, + + InflationProgress result = inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, mRow.getContext(), packageContext, mRow.getHeadsUpManager(), mSmartReplyConstants, mSmartReplyController, mRow.getExistingSmartRepliesAndActions()); + + // wait for image resolver to finish preloading + mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); + + return result; } catch (Exception e) { mError = e; return null; @@ -824,6 +832,9 @@ private void handleError(Exception e) { mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } + + // Cancel any image loading tasks, not useful any more + mRow.getImageResolver().cancelRunningTasks(); } @Override @@ -850,6 +861,9 @@ public void onAsyncInflationFinished(NotificationEntry entry) { // Notify the resolver that the inflation task has finished, // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); + + // Cancel any image loading tasks that have not completed at this point + mRow.getImageResolver().cancelRunningTasks(); } private class RtlEnabledContext extends ContextWrapper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index 4b0e2ffd5d7f..6fdc8a3dce0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -21,10 +21,12 @@ import android.os.AsyncTask; import android.util.Log; -import java.io.IOException; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A cache for inline images of image messages. @@ -57,12 +59,13 @@ public void preload(Uri uri) { } @Override - public Drawable get(Uri uri) { + public Drawable get(Uri uri, long timeoutMs) { Drawable result = null; try { - result = mCache.get(uri).get(); - } catch (InterruptedException | ExecutionException ex) { - Log.d(TAG, "get: Failed get image from " + uri); + result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException | CancellationException ex) { + Log.d(TAG, "get: Failed get image from " + uri + " " + ex); } return result; } @@ -73,6 +76,15 @@ public void purge() { mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); } + @Override + public void cancelRunningTasks() { + mCache.forEach((key, value) -> { + if (value.getStatus() != AsyncTask.Status.FINISHED) { + value.cancel(true); + } + }); + } + private static class PreloadImageTask extends AsyncTask { private final NotificationInlineImageResolver mResolver; @@ -87,7 +99,7 @@ protected Drawable doInBackground(Uri... uris) { try { drawable = mResolver.resolveImage(target); - } catch (IOException | SecurityException ex) { + } catch (Exception ex) { Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 7bd192d850c1..b240855fe6e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -26,6 +26,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.R; @@ -49,6 +50,9 @@ public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + // Timeout for loading images from ImageCache when calling from UI thread + private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; + private final Context mContext; private final ImageCache mImageCache; private Set mWantedUriSet; @@ -132,16 +136,20 @@ Drawable resolveImage(Uri uri) throws IOException { return image; } + /** + * Loads an image from the Uri. + * This method is synchronous and is usually called from the Main thread. + * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. + * + * @param uri Uri of the target image. + * @return drawable of the image, null if loading failed/timeout + */ @Override public Drawable loadImage(Uri uri) { Drawable result = null; try { if (hasCache()) { - // if the uri isn't currently cached, try caching it first - if (!mImageCache.hasEntry(uri)) { - mImageCache.preload((uri)); - } - result = mImageCache.get(uri); + result = loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS); } else { result = resolveImage(uri); } @@ -151,6 +159,14 @@ public Drawable loadImage(Uri uri) { return result; } + private Drawable loadImageFromCache(Uri uri, long timeoutMs) { + // if the uri isn't currently cached, try caching it first + if (!mImageCache.hasEntry(uri)) { + mImageCache.preload((uri)); + } + return mImageCache.get(uri, timeoutMs); + } + /** * Resolve the message list from specified notification and * refresh internal cache according to the result. @@ -222,6 +238,30 @@ Set getWantedUriSet() { return mWantedUriSet; } + /** + * Wait for a maximum timeout for images to finish preloading + * @param timeoutMs total timeout time + */ + void waitForPreloadedImages(long timeoutMs) { + if (!hasCache()) { + return; + } + Set preloadedUris = getWantedUriSet(); + if (preloadedUris != null) { + // Decrement remaining timeout after each image check + long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; + preloadedUris.forEach( + uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); + } + } + + void cancelRunningTasks() { + if (!hasCache()) { + return; + } + mImageCache.cancelRunningTasks(); + } + /** * A interface for internal cache implementation of this resolver. */ @@ -231,7 +271,7 @@ interface ImageCache { * @param uri The uri of the image. * @return Drawable of the image. */ - Drawable get(Uri uri); + Drawable get(Uri uri, long timeoutMs); /** * Set the image resolver that actually resolves image from specified uri. @@ -256,6 +296,11 @@ interface ImageCache { * Purge unnecessary entries in the cache. */ void purge(); + + /** + * Cancel all unfinished image loading tasks + */ + void cancelRunningTasks(); } } From 6662cf616fea1795b872ec9cae4513472f6630db Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 22 Feb 2023 09:38:55 +0100 Subject: [PATCH 144/208] [DO NOT MERGE] Prevent RemoteViews crashing SystemUi Catch canvas drawing exceptions caused by unsuported image sizes. Test: 1. Post a custom view notification with a layout containing an ImageView that references a 5k x 5k image 2. Add an App Widget to the home screen with that has the layout mentioned above as preview/initial layout. Bug: 268193777 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:cfc0b34432ab54e3fa472db5c43e620293f64a5d) Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a --- .../android/appwidget/AppWidgetHostView.java | 39 ++++++++++++++----- .../row/ExpandableNotificationRow.java | 4 +- .../ExpandableNotificationRowController.java | 10 ++++- .../row/NotificationContentView.java | 37 ++++++++++++++++++ ...NotificationEntryManagerInflationTest.java | 4 +- .../row/NotificationTestHelper.java | 5 ++- 6 files changed, 84 insertions(+), 15 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 3fef92b203b6..e2351ee89c42 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -28,6 +28,7 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; @@ -250,19 +251,26 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } + /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by @@ -725,4 +733,15 @@ private OnClickHandler getHandler(OnClickHandler handler) { } }; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 70dd80e71e7a..8b29fb596ea7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -65,6 +65,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.systemui.Dependency; @@ -1612,7 +1613,8 @@ public void initialize( OnAppOpsClickListener onAppOpsClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + IStatusBarService statusBarService) { mAppName = appName; if (mMenuRow == null) { mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 7a6109d2ce78..8c92e730cd66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -22,6 +22,8 @@ import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -69,6 +71,7 @@ public class ExpandableNotificationRowController { private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private final IStatusBarService mStatusBarService; @Inject public ExpandableNotificationRowController(ExpandableNotificationRow view, @@ -84,7 +87,8 @@ public ExpandableNotificationRowController(ExpandableNotificationRow view, NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @DismissRunnable Runnable onDismissRunnable, FalsingManager falsingManager, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + IStatusBarService statusBarService) { mView = view; mActivatableNotificationViewController = activatableNotificationViewController; mMediaManager = mediaManager; @@ -105,6 +109,7 @@ public ExpandableNotificationRowController(ExpandableNotificationRow view, mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; + mStatusBarService = statusBarService; } /** @@ -125,7 +130,8 @@ public void init() { mOnAppOpsClickListener, mFalsingManager, mStatusBarStateController, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mStatusBarService ); mView.setOnDismissRunnable(mOnDismissRunnable); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 1f5b063b0aa2..411c02e3be1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -24,9 +24,11 @@ import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; @@ -45,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.statusbar.MediaTransferManager; import com.android.systemui.statusbar.RemoteInputController; @@ -126,6 +129,8 @@ public class NotificationContentView extends FrameLayout { private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; + private IStatusBarService mStatusBarService; + /** * List of listeners for when content views become inactive (i.e. not the showing view). */ @@ -182,6 +187,7 @@ public NotificationContentView(Context context, AttributeSet attrs) { mMediaTransferManager = new MediaTransferManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); + mStatusBarService = Dependency.get(IStatusBarService.class); initView(); } @@ -1965,4 +1971,35 @@ public int getOriginalIconColor() { } return Notification.COLOR_INVALID; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + mStatusBarNotification.getPackageName(), + mStatusBarNotification.getTag(), + mStatusBarNotification.getId(), + mStatusBarNotification.getUid(), + mStatusBarNotification.getInitialPid(), + exception.getMessage(), + mStatusBarNotification.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 299dd0c92ff2..484849f735df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -40,6 +40,7 @@ import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.test.filters.SmallTest; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -252,7 +253,8 @@ public void setUp() { true, null, mFalsingManager, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mock(IStatusBarService.class) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 0c6409b38d21..bb726191a09d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -43,6 +43,7 @@ import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; @@ -424,7 +425,9 @@ private ExpandableNotificationRow generateRow( mock(ExpandableNotificationRow.OnAppOpsClickListener.class), mock(FalsingManager.class), mStatusBarStateController, - mPeopleNotificationIdentifier); + mPeopleNotificationIdentifier, + mock(IStatusBarService.class)); + row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); inflateAndWait(entry); From e1f0b624cfc71ce045c4a021655eb957d4355e70 Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: [PATCH 145/208] Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:92114886bdce8467c52c655c186f3e7ab1e134d8) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../accounts/AccountManagerService.java | 18 +++++++--- .../accounts/AccountManagerServiceTest.java | 36 +++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 8d4337accc20..545373c289bc 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4798,10 +4798,6 @@ protected boolean checkKeyIntent(int authUid, Bundle bundle) { if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4848,7 +4844,19 @@ private boolean checkKeyIntentParceledCorrectly(Bundle bundle) { if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index ac305a93bfb8..f7b7a5801b42 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -691,6 +692,41 @@ public void testStartAddAccountSessionReturnWithValidIntent() throws Exception { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public Bundle startAddAccountSession( String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public Bundle startAddAccountSession( intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { From 95cc3caf71b23d12aff75056f03b5006afa283e3 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Wed, 21 Sep 2022 22:13:01 +0000 Subject: [PATCH 146/208] Handle invalid data during job loading. Catch exceptions that may be thrown if invalid data ended up in the persisted job file. Bug: 246541702 Bug: 246542132 Bug: 246542285 Bug: 246542330 Test: install test app with invalid job config, start app to schedule job, then reboot device (cherry picked from commit c98fb42b480b3beedc2d94de6110f50212c4aa0b) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:df1ba00dd9f64a3ae9a9e05979dfae6a15c7e203) Merged-In: Id0ceba345942baf21177f687b8dd85ef001c0a9e Change-Id: Id0ceba345942baf21177f687b8dd85ef001c0a9e --- .../java/com/android/server/job/JobStore.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index 2f5f555817ec..146d68fdd653 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -687,6 +687,10 @@ public void run() { } } catch (XmlPullParserException | IOException e) { Slog.wtf(TAG, "Error jobstore xml.", e); + } catch (Exception e) { + // Crashing at this point would result in a boot loop, so live with a general + // Exception for system stability's sake. + Slog.wtf(TAG, "Unexpected exception", e); } finally { if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once. mPersistInfo.countAllJobsLoaded = numJobs; @@ -817,6 +821,15 @@ private JobStatus restoreJobFromXml(boolean rtcIsGood, XmlPullParser parser) } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; + } catch (XmlPullParserException e) { + Slog.d(TAG, "Error Parser Exception.", e); + return null; + } catch (IOException e) { + Slog.d(TAG, "Error I/O Exception.", e); + return null; + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Constraints contained invalid data", e); + return null; } parser.next(); // Consume @@ -912,8 +925,14 @@ private JobStatus restoreJobFromXml(boolean rtcIsGood, XmlPullParser parser) return null; } - PersistableBundle extras = PersistableBundle.restoreFromXml(parser); - jobBuilder.setExtras(extras); + final PersistableBundle extras; + try { + extras = PersistableBundle.restoreFromXml(parser); + jobBuilder.setExtras(extras); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Persisted extras contained invalid data", e); + return null; + } parser.nextTag(); // Consume final JobInfo builtJob; @@ -959,7 +978,8 @@ private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberF return new JobInfo.Builder(jobId, cname); } - private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { + private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) + throws XmlPullParserException, IOException { String val; final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); From 18b16a194ead9af6a3a5149d3fc5b7cd324da9d8 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 7 Mar 2023 15:44:49 -0500 Subject: [PATCH 147/208] Allow filtering of services Test: ServiceListingTest Bug: 260570119 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:44dcb8351e61f4b3a63ec68fa5d8490501e8a823) Merged-In: Ib4740ba401667de62fa1a33334c2c1fbee25b760 Change-Id: Ib4740ba401667de62fa1a33334c2c1fbee25b760 --- .../applications/ServiceListing.java | 17 +++- .../SettingsLib/tests/robotests/Android.bp | 1 + .../applications/ServiceListingTest.java | 98 ++++++++++++++++++- 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java index bd9e760acfda..c8bcabff1094 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.function.Predicate; /** * Class for managing services matching a given intent and requesting a given permission. @@ -51,12 +52,13 @@ public class ServiceListing { private final HashSet mEnabledServices = new HashSet<>(); private final List mServices = new ArrayList<>(); private final List mCallbacks = new ArrayList<>(); + private final Predicate mValidator; private boolean mListening; private ServiceListing(Context context, String tag, String setting, String intentAction, String permission, String noun, - boolean addDeviceLockedFlags) { + boolean addDeviceLockedFlags, Predicate validator) { mContentResolver = context.getContentResolver(); mContext = context; mTag = tag; @@ -65,6 +67,7 @@ private ServiceListing(Context context, String tag, mPermission = permission; mNoun = noun; mAddDeviceLockedFlags = addDeviceLockedFlags; + mValidator = validator; } public void addCallback(Callback callback) { @@ -137,7 +140,6 @@ public void reload() { final PackageManager pmWrapper = mContext.getPackageManager(); List installedServices = pmWrapper.queryIntentServicesAsUser( new Intent(mIntentAction), flags, user); - for (ResolveInfo resolveInfo : installedServices) { ServiceInfo info = resolveInfo.serviceInfo; @@ -148,6 +150,9 @@ public void reload() { + mPermission); continue; } + if (mValidator != null && !mValidator.test(info)) { + continue; + } mServices.add(info); } for (Callback callback : mCallbacks) { @@ -194,6 +199,7 @@ public static class Builder { private String mPermission; private String mNoun; private boolean mAddDeviceLockedFlags = false; + private Predicate mValidator; public Builder(Context context) { mContext = context; @@ -224,6 +230,11 @@ public Builder setNoun(String noun) { return this; } + public Builder setValidator(Predicate validator) { + mValidator = validator; + return this; + } + /** * Set to true to add support for both MATCH_DIRECT_BOOT_AWARE and * MATCH_DIRECT_BOOT_UNAWARE flags when querying PackageManager. Required to get results @@ -236,7 +247,7 @@ public Builder setAddDeviceLockedFlags(boolean addDeviceLockedFlags) { public ServiceListing build() { return new ServiceListing(mContext, mTag, mSetting, mIntentAction, mPermission, mNoun, - mAddDeviceLockedFlags); + mAddDeviceLockedFlags, mValidator); } } } diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp index 3756c3bf7704..723160d68771 100644 --- a/packages/SettingsLib/tests/robotests/Android.bp +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -34,6 +34,7 @@ android_robolectric_test { srcs: ["src/**/*.java"], static_libs: [ "SettingsLib-robo-testutils", + "androidx.test.core", ], java_resource_dirs: ["config"], instrumentation_for: "SettingsLibShell", diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java index f7fd25b9fb7d..7ff0988c494d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java @@ -18,20 +18,35 @@ import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.provider.Settings; +import androidx.test.core.app.ApplicationProvider; + +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.List; + @RunWith(RobolectricTestRunner.class) public class ServiceListingTest { @@ -39,16 +54,97 @@ public class ServiceListingTest { private static final String TEST_INTENT = "com.example.intent"; private ServiceListing mServiceListing; + private Context mContext; + private PackageManager mPm; @Before public void setUp() { - mServiceListing = new ServiceListing.Builder(RuntimeEnvironment.application) + mPm = mock(PackageManager.class); + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getPackageManager()).thenReturn(mPm); + + mServiceListing = new ServiceListing.Builder(mContext) + .setTag("testTag") + .setSetting(TEST_SETTING) + .setNoun("testNoun") + .setIntentAction(TEST_INTENT) + .setPermission("testPermission") + .build(); + } + + @Test + public void testValidator() { + ServiceInfo s1 = new ServiceInfo(); + s1.permission = "testPermission"; + s1.packageName = "pkg"; + ServiceInfo s2 = new ServiceInfo(); + s2.permission = "testPermission"; + s2.packageName = "pkg2"; + ResolveInfo r1 = new ResolveInfo(); + r1.serviceInfo = s1; + ResolveInfo r2 = new ResolveInfo(); + r2.serviceInfo = s2; + + when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn( + ImmutableList.of(r1, r2)); + + mServiceListing = new ServiceListing.Builder(mContext) + .setTag("testTag") + .setSetting(TEST_SETTING) + .setNoun("testNoun") + .setIntentAction(TEST_INTENT) + .setValidator(info -> { + if (info.packageName.equals("pkg")) { + return true; + } + return false; + }) + .setPermission("testPermission") + .build(); + ServiceListing.Callback callback = mock(ServiceListing.Callback.class); + mServiceListing.addCallback(callback); + mServiceListing.reload(); + + verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt()); + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(callback, times(1)).onServicesReloaded(captor.capture()); + + assertThat(captor.getValue().size()).isEqualTo(1); + assertThat(captor.getValue().get(0)).isEqualTo(s1); + } + + @Test + public void testNoValidator() { + ServiceInfo s1 = new ServiceInfo(); + s1.permission = "testPermission"; + s1.packageName = "pkg"; + ServiceInfo s2 = new ServiceInfo(); + s2.permission = "testPermission"; + s2.packageName = "pkg2"; + ResolveInfo r1 = new ResolveInfo(); + r1.serviceInfo = s1; + ResolveInfo r2 = new ResolveInfo(); + r2.serviceInfo = s2; + + when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn( + ImmutableList.of(r1, r2)); + + mServiceListing = new ServiceListing.Builder(mContext) .setTag("testTag") .setSetting(TEST_SETTING) .setNoun("testNoun") .setIntentAction(TEST_INTENT) .setPermission("testPermission") .build(); + ServiceListing.Callback callback = mock(ServiceListing.Callback.class); + mServiceListing.addCallback(callback); + mServiceListing.reload(); + + verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt()); + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(callback, times(1)).onServicesReloaded(captor.capture()); + + assertThat(captor.getValue().size()).isEqualTo(2); } @Test From 38f3ebe9424dcf46b9bc7ea8b4019edd72827bb5 Mon Sep 17 00:00:00 2001 From: Alex Johnston Date: Wed, 8 Mar 2023 22:28:28 +0000 Subject: [PATCH 148/208] Enforce DevicePolicyManager.setUserControlDisabledPackages in AppStandbyController When deciding an app's standby bucket, check if the app has its user control disabled by an IT admin. If so, the app should be the exempted restricted bucket. Bug: 272042183 Test: atest AppStandbyControllerTests (cherry picked from commit 269fcb6873dee199dd8023831f882aafff1f6291) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:552dbbc142324408ab8d53cfb5c82a9395f9532c) Merged-In: I4279dc37f0e17aedb1c2a87468478248443a253e Change-Id: I4279dc37f0e17aedb1c2a87468478248443a253e --- .../server/usage/AppStandbyInternal.java | 2 + .../server/usage/AppStandbyController.java | 40 +++++++++++++++++++ .../app/admin/DevicePolicyManager.java | 3 +- .../app/usage/UsageStatsManagerInternal.java | 10 +++++ .../DevicePolicyManagerService.java | 6 ++- .../usage/AppStandbyControllerTests.java | 38 ++++++++++++++++++ .../server/usage/UsageStatsService.java | 5 +++ 7 files changed, 102 insertions(+), 2 deletions(-) diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index e15f0f37fc62..0e98bf0e8e00 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -146,6 +146,8 @@ void restrictApp(@NonNull String packageName, int userId, void setActiveAdminApps(Set adminPkgs, int userId); + void setAdminProtectedPackages(Set packageNames, int userId); + void onAdminDataAvailable(); void clearCarrierPrivilegedApps(); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 36ccaf9c6fb8..403e8b5547bd 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -233,6 +233,10 @@ static class Lock {} @GuardedBy("mActiveAdminApps") private final SparseArray> mActiveAdminApps = new SparseArray<>(); + /** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */ + @GuardedBy("mAdminProtectedPackages") + private final SparseArray> mAdminProtectedPackages = new SparseArray<>(); + /** * Set of system apps that are headless (don't have any declared activities, enabled or * disabled). Presence in this map indicates that the app is a headless system app. @@ -1019,6 +1023,9 @@ public void onUserRemoved(int userId) { synchronized (mActiveAdminApps) { mActiveAdminApps.remove(userId); } + synchronized (mAdminProtectedPackages) { + mAdminProtectedPackages.remove(userId); + } } } @@ -1108,6 +1115,10 @@ private int getAppMinBucket(String packageName, int appId, int userId) { return STANDBY_BUCKET_EXEMPTED; } + if (isAdminProtectedPackages(packageName, userId)) { + return STANDBY_BUCKET_EXEMPTED; + } + if (isActiveNetworkScorer(packageName)) { return STANDBY_BUCKET_EXEMPTED; } @@ -1510,6 +1521,17 @@ boolean isActiveDeviceAdmin(String packageName, int userId) { } } + private boolean isAdminProtectedPackages(String packageName, int userId) { + synchronized (mAdminProtectedPackages) { + if (mAdminProtectedPackages.contains(UserHandle.USER_ALL) + && mAdminProtectedPackages.get(UserHandle.USER_ALL).contains(packageName)) { + return true; + } + return mAdminProtectedPackages.contains(userId) + && mAdminProtectedPackages.get(userId).contains(packageName); + } + } + @Override public void addActiveDeviceAdmin(String adminPkg, int userId) { synchronized (mActiveAdminApps) { @@ -1533,6 +1555,17 @@ public void setActiveAdminApps(Set adminPkgs, int userId) { } } + @Override + public void setAdminProtectedPackages(Set packageNames, int userId) { + synchronized (mAdminProtectedPackages) { + if (packageNames == null || packageNames.isEmpty()) { + mAdminProtectedPackages.remove(userId); + } else { + mAdminProtectedPackages.put(userId, packageNames); + } + } + } + @Override public void onAdminDataAvailable() { mAdminDataAvailableLatch.countDown(); @@ -1555,6 +1588,13 @@ Set getActiveAdminAppsForTest(int userId) { } } + @VisibleForTesting + Set getAdminProtectedPackagesForTest(int userId) { + synchronized (mAdminProtectedPackages) { + return mAdminProtectedPackages.get(userId); + } + } + /** * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, * returns {@code false}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index aa6d74fb8c39..67d534b6fc69 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11996,7 +11996,8 @@ public boolean startViewCalendarEventInManagedProfile(long eventId, long start, /** * Called by Device owner to disable user control over apps. User will not be able to clear - * app data or force-stop packages. + * app data or force-stop packages. Packages with user control disabled are exempted from + * App Standby Buckets. * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param packages The package names for the apps. diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index fa84427ac064..920fe1b62417 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -198,6 +198,16 @@ public abstract void reportLocusUpdate(@NonNull ComponentName activity, @UserIdI */ public abstract void setActiveAdminApps(Set adminApps, int userId); + /** + * Called by DevicePolicyManagerService to inform about the protected packages for a user. + * User control will be disabled for protected packages. + * + * @param packageNames the set of protected packages for {@code userId}. + * @param userId the userId to which the protected packages belong. + */ + public abstract void setAdminProtectedPackages(@Nullable Set packageNames, + @UserIdInt int userId); + /** * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and * pushed to UsageStatsService. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index d9baa4131d42..b82888a53c6e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4021,7 +4021,9 @@ private void loadSettingsLocked(DevicePolicyData policy, int userHandle) { updateMaximumTimeToLockLocked(userHandle); updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle); updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle); - updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages); + if (userHandle == UserHandle.USER_SYSTEM) { + updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages); + } if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); } @@ -4049,6 +4051,8 @@ private void updateLockTaskPackagesLocked(List packages, int userId) { private void updateUserControlDisabledPackagesLocked(List packages) { mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages); + mUsageStatsManagerInternal.setAdminProtectedPackages( + new ArraySet(packages), UserHandle.USER_ALL); } private void updateLockTaskFeaturesLocked(int flags, int userId) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 1ced467dffa1..8da4fed1ecf0 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -125,6 +125,9 @@ public class AppStandbyControllerTests { private static final String ADMIN_PKG2 = "com.android.admin2"; private static final String ADMIN_PKG3 = "com.android.admin3"; + private static final String ADMIN_PROTECTED_PKG = "com.android.admin.protected"; + private static final String ADMIN_PROTECTED_PKG2 = "com.android.admin.protected2"; + private static final long MINUTE_MS = 60 * 1000; private static final long HOUR_MS = 60 * MINUTE_MS; private static final long DAY_MS = 24 * HOUR_MS; @@ -1369,6 +1372,19 @@ public void isActiveDeviceAdmin() { assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID); } + @Test + public void testSetAdminProtectedPackages() { + assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); + assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); + + setAdminProtectedPackages(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); + assertAdminProtectedPackagesForTest(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); + assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); + + setAdminProtectedPackages(USER_ID, (String[]) null); + assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); + } + @Test public void testUserInteraction_CrossProfile() throws Exception { mInjector.mRunningUsers = new int[] {USER_ID, USER_ID2, USER_ID3}; @@ -1593,6 +1609,28 @@ private void setActiveAdmins(int userId, String... admins) { mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId); } + private void setAdminProtectedPackages(int userId, String... packageNames) { + Set adminProtectedPackages = packageNames != null ? new ArraySet<>( + Arrays.asList(packageNames)) : null; + mController.setAdminProtectedPackages(adminProtectedPackages, userId); + } + + private void assertAdminProtectedPackagesForTest(int userId, String... packageNames) { + final Set actualAdminProtectedPackages = + mController.getAdminProtectedPackagesForTest(userId); + if (packageNames == null) { + if (actualAdminProtectedPackages != null && !actualAdminProtectedPackages.isEmpty()) { + fail("Admin protected packages should be null; " + getAdminAppsStr(userId, + actualAdminProtectedPackages)); + } + return; + } + assertEquals(packageNames.length, actualAdminProtectedPackages.size()); + for (String adminProtectedPackage : packageNames) { + assertTrue(actualAdminProtectedPackages.contains(adminProtectedPackage)); + } + } + private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { rearmLatch(pkg); mController.setAppStandbyBucket(pkg, user, bucket, reason); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a54f263c32ae..c0b3feeaffc2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2302,6 +2302,11 @@ public void setActiveAdminApps(Set packageNames, int userId) { mAppStandby.setActiveAdminApps(packageNames, userId); } + @Override + public void setAdminProtectedPackages(Set packageNames, int userId) { + mAppStandby.setAdminProtectedPackages(packageNames, userId); + } + @Override public void onAdminDataAvailable() { mAppStandby.onAdminDataAvailable(); From a61b7a760360e38758045d2a4eeeab20ef2a5750 Mon Sep 17 00:00:00 2001 From: Jeff DeCew Date: Fri, 24 Mar 2023 16:15:24 +0000 Subject: [PATCH 149/208] [RESTRICT AUTOMERGE] Add BubbleMetadata detection to block FSI Bug: 274759612 Test: atest NotificationInterruptStateProviderImplTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c60e264a551df9f880fd73683321b7e821429da7) Merged-In: I40e1aa6377b8a60d91cb2f4189df1e9a4a4578a2 Change-Id: I40e1aa6377b8a60d91cb2f4189df1e9a4a4578a2 --- ...NotificationInterruptStateProviderImpl.java | 15 +++++++++++++++ ...ficationInterruptStateProviderImplTest.java | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 8e236ea54f10..5158f2eb0324 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -18,6 +18,7 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE; +import android.app.Notification; import android.app.NotificationManager; import android.content.ContentResolver; import android.database.ContentObserver; @@ -210,6 +211,20 @@ public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { return false; } + // If the notification has suppressive BubbleMetadata, block FSI and warn. + Notification.BubbleMetadata bubbleMetadata = sbn.getNotification().getBubbleMetadata(); + if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed()) { + // b/274759612: Detect and report an event when a notification has both an FSI and a + // suppressive BubbleMetadata, and now correctly block the FSI from firing. + final int uid = entry.getSbn().getUid(); + android.util.EventLog.writeEvent(0x534e4554, "274759612", uid, "bubbleMetadata"); + if (DEBUG) { + Log.w(TAG, "No FullScreenIntent: WARNING: BubbleMetadata may prevent HUN: " + + entry.getKey()); + } + return false; + } + // If the screen is off, then launch the FullScreenIntent if (!mPowerManager.isInteractive()) { if (DEBUG) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index f4455ddb25bc..fb945f3ac3ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -435,9 +435,27 @@ public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteExceptio .isFalse(); } + + @Test + public void testShouldNotFullScreen_isSuppressedByBubbleMetadata() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") + .setSuppressNotification(true).build(); + entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); + when(mPowerManager.isInteractive()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + } + @Test public void testShouldFullScreen_notInteractive() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") + .setSuppressNotification(false).build(); + entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); when(mPowerManager.isInteractive()).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mStatusBarStateController.getState()).thenReturn(SHADE); From 5dd1bc5e059c0126f60ce2f004e7cc592288666c Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Thu, 23 Feb 2023 13:23:37 -0800 Subject: [PATCH 150/208] Remove Activity if it enters PiP without window This is to prevent malicious app entering PiP without being visible first, like blocking onResume from completion. Which in turn leaves the PiP window in limbo and non-interactable. Bug: 265293293 Test: atest PinnedStackTests (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4fad1456409b79d6e649a29d5116a4fe3160bd21) Merged-In: I458a9508662e72a1adb9d9818105f2e9d7096d44 Change-Id: I458a9508662e72a1adb9d9818105f2e9d7096d44 --- .../core/java/com/android/server/wm/ActivityRecord.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e5a069847f5c..9bbb090bc6c9 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1200,6 +1200,12 @@ void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) { } ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS, true /* ignoreVisibility */); + if (inPictureInPictureMode && findMainWindow() == null) { + // Prevent malicious app entering PiP without valid WindowState, which can in turn + // result a non-touchable PiP window since the InputConsumer for PiP requires it. + EventLog.writeEvent(0x534e4554, "265293293", -1, ""); + removeImmediately(); + } } } From 365b1b7ea412ce06fc692c35c82c7f9a19b9c177 Mon Sep 17 00:00:00 2001 From: "Nate(Qiang) Jiang" Date: Wed, 26 Oct 2022 21:52:34 +0000 Subject: [PATCH 151/208] Passpoint Add more check to limit the config size Bug: 245299920 Test: atest con.android.server.wifi (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e1a80210f3f0391c989a2a86fd4aef739bf2574c) Merged-In: I97522ce3607547c10025caa107cd1a40455a9c5d Change-Id: I97522ce3607547c10025caa107cd1a40455a9c5d --- .../wifi/hotspot2/PasspointConfiguration.java | 59 +++++++++++++- .../net/wifi/hotspot2/pps/Credential.java | 10 ++- .../android/net/wifi/hotspot2/pps/HomeSp.java | 79 ++++++++++++++++++- .../hotspot2/PasspointConfigurationTest.java | 2 +- 4 files changed, 146 insertions(+), 4 deletions(-) diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index d1d1780a25fd..09f91d688f7b 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -60,8 +60,42 @@ public final class PasspointConfiguration implements Parcelable { /** * Maximum bytes for URL string. + * @hide + */ + public static final int MAX_URL_BYTES = 2048; + + /** + * Maximum size for match entry, just to limit the size of the Passpoint config. + * @hide + */ + public static final int MAX_NUMBER_OF_ENTRIES = 16; + + /** + * Maximum size for OI entry. + * The spec allows a string of up to 255 characters, with comma delimited numbers like + * 001122,334455. So with minimum OI size of 7, the maximum amount of OIs is 36. + * @hide + */ + public static final int MAX_NUMBER_OF_OI = 36; + + + /** + * Maximum bytes for a string entry like FQDN and friendly name. + * @hide + */ + public static final int MAX_STRING_LENGTH = 255; + + /** + * HESSID is 48 bit. + * @hide */ - private static final int MAX_URL_BYTES = 1023; + public static final long MAX_HESSID_VALUE = ((long) 1 << 48) - 1; + + /** + * Organization Identifiers is 3 or 5 Octets. 24 or 36 bit. + * @hide + */ + public static final long MAX_OI_VALUE = ((long) 1 << 40) - 1; /** * Integer value used for indicating null value in the Parcel. @@ -760,7 +794,30 @@ private boolean validateForCommonR1andR2() { return false; } + if (mAaaServerTrustedNames != null) { + if (mAaaServerTrustedNames.length > MAX_NUMBER_OF_ENTRIES) { + Log.d(TAG, "Too many AaaServerTrustedNames"); + return false; + } + for (String fqdn : mAaaServerTrustedNames) { + if (fqdn.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.d(TAG, "AaaServerTrustedNames is too long"); + return false; + } + } + } + if (mSubscriptionType != null) { + if (mSubscriptionType.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.d(TAG, "SubscriptionType is too long"); + return false; + } + } + if (mTrustRootCertList != null) { + if (mTrustRootCertList.size() > MAX_NUMBER_OF_ENTRIES) { + Log.d(TAG, "Too many TrustRootCert"); + return false; + } for (Map.Entry entry : mTrustRootCertList.entrySet()) { String url = entry.getKey(); byte[] certFingerprint = entry.getValue(); diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 282757ac5a14..82ab29e6691b 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -16,6 +16,8 @@ package android.net.wifi.hotspot2.pps; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_STRING_LENGTH; + import android.net.wifi.EAPConstants; import android.net.wifi.ParcelUtil; import android.os.Parcel; @@ -413,7 +415,13 @@ public boolean validate() { + mPassword.getBytes(StandardCharsets.UTF_8).length); return false; } - + if (mSoftTokenApp != null) { + if (mSoftTokenApp.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.d(TAG, "app name exceeding maximum length: " + + mSoftTokenApp.getBytes(StandardCharsets.UTF_8).length); + return false; + } + } // Only supports EAP-TTLS for user credential. if (mEapType != EAPConstants.EAP_TTLS) { Log.d(TAG, "Invalid EAP Type for user credential: " + mEapType); diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java index 8f34579f6a5d..05a0ec913066 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java @@ -16,6 +16,13 @@ package android.net.wifi.hotspot2.pps; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_HESSID_VALUE; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_ENTRIES; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_OI; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_OI_VALUE; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_STRING_LENGTH; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_URL_BYTES; + import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -342,16 +349,86 @@ public boolean validate() { Log.d(TAG, "Missing FQDN"); return false; } + if (mFqdn.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.d(TAG, "FQDN is too long"); + return false; + } if (TextUtils.isEmpty(mFriendlyName)) { Log.d(TAG, "Missing friendly name"); return false; } + if (mFriendlyName.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.d(TAG, "Friendly name is too long"); + return false; + } // Verify SSIDs specified in the NetworkID if (mHomeNetworkIds != null) { + if (mHomeNetworkIds.size() > MAX_NUMBER_OF_ENTRIES) { + Log.d(TAG, "too many SSID in HomeNetworkIDs"); + return false; + } for (Map.Entry entry : mHomeNetworkIds.entrySet()) { if (entry.getKey() == null || entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { - Log.d(TAG, "Invalid SSID in HomeNetworkIDs"); + Log.d(TAG, "SSID is too long in HomeNetworkIDs"); + return false; + } + if (entry.getValue() != null + && (entry.getValue() > MAX_HESSID_VALUE || entry.getValue() < 0)) { + Log.d(TAG, "HESSID is out of range"); + return false; + } + } + } + if (mIconUrl != null && mIconUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { + Log.d(TAG, "Icon URL is too long"); + return false; + } + if (mMatchAllOis != null) { + if (mMatchAllOis.length > MAX_NUMBER_OF_OI) { + Log.d(TAG, "too many match all Organization Identifiers in the profile"); + return false; + } + for (long oi : mMatchAllOis) { + if (oi > MAX_OI_VALUE || oi < 0) { + Log.d(TAG, "Organization Identifiers is out of range"); + return false; + } + } + } + if (mMatchAnyOis != null) { + if (mMatchAnyOis.length > MAX_NUMBER_OF_OI) { + Log.d(TAG, "too many match any Organization Identifiers in the profile"); + return false; + } + for (long oi : mMatchAnyOis) { + if (oi > MAX_OI_VALUE || oi < 0) { + Log.d(TAG, "Organization Identifiers is out of range"); + return false; + } + } + } + if (mRoamingConsortiumOis != null) { + if (mRoamingConsortiumOis.length > MAX_NUMBER_OF_OI) { + Log.d(TAG, "too many Roaming Consortium Organization Identifiers in the " + + "profile"); + return false; + } + for (long oi : mRoamingConsortiumOis) { + if (oi > MAX_OI_VALUE || oi < 0) { + Log.d(TAG, "Organization Identifiers is out of range"); + return false; + } + } + } + if (mOtherHomePartners != null) { + if (mOtherHomePartners.length > MAX_NUMBER_OF_ENTRIES) { + Log.d(TAG, "too many other home partners in the profile"); + return false; + } + for (String fqdn : mOtherHomePartners) { + if (fqdn.length() > MAX_STRING_LENGTH) { + Log.d(TAG, "FQDN is too long in OtherHomePartners"); return false; } } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index 638efb9f14ee..59ffcd5ed169 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -41,7 +41,7 @@ */ @SmallTest public class PasspointConfigurationTest { - private static final int MAX_URL_BYTES = 1023; + private static final int MAX_URL_BYTES = 2048; private static final int CERTIFICATE_FINGERPRINT_BYTES = 32; /** From 88b9366f89c6a5dc127ba1ab5f9d98f0e97bff76 Mon Sep 17 00:00:00 2001 From: Lucas Lin Date: Fri, 3 Mar 2023 08:13:50 +0000 Subject: [PATCH 152/208] Sanitize VPN label to prevent HTML injection This commit will try to sanitize the content of VpnDialog. This commit creates a function which will try to sanitize the VPN label, if the sanitized VPN label is different from the original one, which means the VPN label might contain HTML tag or the VPN label violates the words restriction(may contain some wording which will mislead the user). For this kind of case, show the package name instead of the VPN label to prevent misleading the user. The malicious VPN app might be able to add a large number of line breaks with HTML in order to hide the system-displayed text from the user in the connection request dialog. Thus, sanitizing the content of the dialog is needed. Bug: 204554636 Test: N/A (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2178216b98bf9865edee198f45192f0b883624ab) Merged-In: I8eb890fd2e5797d8d6ab5b12f9c628bc9616081d Change-Id: I8eb890fd2e5797d8d6ab5b12f9c628bc9616081d --- packages/VpnDialogs/res/values/strings.xml | 28 ++++++++++ .../com/android/vpndialogs/ConfirmDialog.java | 53 +++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml index 443a9bc33b90..b4166f0bedfd 100644 --- a/packages/VpnDialogs/res/values/strings.xml +++ b/packages/VpnDialogs/res/values/strings.xml @@ -89,4 +89,32 @@ without any consequences. [CHAR LIMIT=20] --> Dismiss + + + %1$s… ( + %2$s) + + + + + %1$s ( + %2$s) + diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index e66f2cc17a7f..a1fbffe50af7 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -44,12 +44,18 @@ public class ConfirmDialog extends AlertActivity implements DialogInterface.OnClickListener, ImageGetter { private static final String TAG = "VpnConfirm"; + // Usually the label represents the app name, 150 code points might be enough to display the app + // name, and 150 code points won't cover the warning message from VpnDialog. + static final int MAX_VPN_LABEL_LENGTH = 150; + @VpnManager.VpnType private final int mVpnType; private String mPackage; private IConnectivityManager mService; + private View mView; + public ConfirmDialog() { this(VpnManager.TYPE_VPN_SERVICE); } @@ -58,6 +64,42 @@ public ConfirmDialog(@VpnManager.VpnType int vpnType) { mVpnType = vpnType; } + /** + * This function will use the string resource to combine the VPN label and the package name. + * + * If the VPN label violates the length restriction, the first 30 code points of VPN label and + * the package name will be returned. Or return the VPN label and the package name directly if + * the VPN label doesn't violate the length restriction. + * + * The result will be something like, + * - ThisIsAVeryLongVpnAppNameWhich... (com.vpn.app) + * if the VPN label violates the length restriction. + * or + * - VpnLabelWith<br>HtmlTag (com.vpn.app) + * if the VPN label doesn't violate the length restriction. + * + */ + private String getSimplifiedLabel(String vpnLabel, String packageName) { + if (vpnLabel.codePointCount(0, vpnLabel.length()) > 30) { + return getString(R.string.sanitized_vpn_label_with_ellipsis, + vpnLabel.substring(0, vpnLabel.offsetByCodePoints(0, 30)), + packageName); + } + + return getString(R.string.sanitized_vpn_label, vpnLabel, packageName); + } + + protected String getSanitizedVpnLabel(String vpnLabel, String packageName) { + final String sanitizedVpnLabel = Html.escapeHtml(vpnLabel); + final boolean exceedMaxVpnLabelLength = sanitizedVpnLabel.codePointCount(0, + sanitizedVpnLabel.length()) > MAX_VPN_LABEL_LENGTH; + if (exceedMaxVpnLabelLength || !vpnLabel.equals(sanitizedVpnLabel)) { + return getSimplifiedLabel(sanitizedVpnLabel, packageName); + } + + return sanitizedVpnLabel; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -80,15 +122,16 @@ protected void onCreate(Bundle savedInstanceState) { finish(); return; } - View view = View.inflate(this, R.layout.confirm, null); - ((TextView) view.findViewById(R.id.warning)).setText( - Html.fromHtml(getString(R.string.warning, getVpnLabel()), - this, null /* tagHandler */)); + mView = View.inflate(this, R.layout.confirm, null); + ((TextView) mView.findViewById(R.id.warning)).setText( + Html.fromHtml(getString(R.string.warning, getSanitizedVpnLabel( + getVpnLabel().toString(), mPackage)), + this /* imageGetter */, null /* tagHandler */)); mAlertParams.mTitle = getText(R.string.prompt); mAlertParams.mPositiveButtonText = getText(android.R.string.ok); mAlertParams.mPositiveButtonListener = this; mAlertParams.mNegativeButtonText = getText(android.R.string.cancel); - mAlertParams.mView = view; + mAlertParams.mView = mView; setupAlert(); getWindow().setCloseOnTouchOutside(false); From d0a8bb32b7a55375564aabc445cb9cbf53958861 Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Fri, 31 Mar 2023 21:31:22 +0000 Subject: [PATCH 153/208] Limit the number of supported v1 and v2 signers The v1 and v2 APK Signature Schemes support multiple signers; this was intended to allow multiple entities to sign an APK. Previously, the platform had no limits placed on the number of signers supported in an APK, but this commit sets a hard limit of 10 supported signers for these signature schemes to ensure a large number of signers does not place undue burden on the platform. Bug: 266580022 Test: Manually verified the platform only allowed an APK with the maximum number of supported signers. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6f6ee8a55f37c2b8c0df041b2bd53ec928764597) Merged-In: I6aa86b615b203cdc69d58a593ccf8f18474ca091 Change-Id: I6aa86b615b203cdc69d58a593ccf8f18474ca091 --- .../util/apk/ApkSignatureSchemeV2Verifier.java | 10 ++++++++++ core/java/android/util/jar/StrictJarVerifier.java | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 346fe293d7ae..f0c20bec7d7b 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -75,6 +75,11 @@ public class ApkSignatureSchemeV2Verifier { private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; + /** + * The maximum number of signers supported by the v2 APK signature scheme. + */ + private static final int MAX_V2_SIGNERS = 10; + /** * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature. * @@ -183,6 +188,11 @@ private static VerifiedSigner verify( } while (signers.hasRemaining()) { signerCount++; + if (signerCount > MAX_V2_SIGNERS) { + throw new SecurityException( + "APK Signature Scheme v2 only supports a maximum of " + MAX_V2_SIGNERS + + " signers"); + } try { ByteBuffer signer = getLengthPrefixedSlice(signers); X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory); diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java index 45254908c5c9..a6aca330d323 100644 --- a/core/java/android/util/jar/StrictJarVerifier.java +++ b/core/java/android/util/jar/StrictJarVerifier.java @@ -78,6 +78,11 @@ class StrictJarVerifier { "SHA1", }; + /** + * The maximum number of signers supported by the JAR signature scheme. + */ + private static final int MAX_JAR_SIGNERS = 10; + private final String jarName; private final StrictJarManifest manifest; private final HashMap metaEntries; @@ -293,10 +298,16 @@ synchronized boolean readCertificates() { return false; } + int signerCount = 0; Iterator it = metaEntries.keySet().iterator(); while (it.hasNext()) { String key = it.next(); if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) { + if (++signerCount > MAX_JAR_SIGNERS) { + throw new SecurityException( + "APK Signature Scheme v1 only supports a maximum of " + MAX_JAR_SIGNERS + + " signers"); + } verifyCertificate(key); it.remove(); } From 42d0edf1684bc883ba9af28aa3b62ad9cab58df7 Mon Sep 17 00:00:00 2001 From: "Nate(Qiang) Jiang" Date: Thu, 13 Apr 2023 21:20:37 +0000 Subject: [PATCH 154/208] DO NOT MERGE: Add size check on PPS#policy Bug: 275340417 Test: atest android.net.wifi (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d1afd2c47d086e0365bf6814a9f47555c294769f) Merged-In: I6e6128b7ed5327da8dbc9186a82bef0f2e4197bb Change-Id: I6e6128b7ed5327da8dbc9186a82bef0f2e4197bb --- .../android/net/wifi/hotspot2/pps/Policy.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java index b0a2cc397c53..4bdacebda060 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java @@ -16,6 +16,9 @@ package android.net.wifi.hotspot2.pps; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_ENTRIES; +import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_STRING_LENGTH; + import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -269,11 +272,19 @@ public String toString() { */ public boolean validate() { if (TextUtils.isEmpty(mFqdn)) { - Log.d(TAG, "Missing FQDN"); + Log.e(TAG, "Missing FQDN"); + return false; + } + if (mFqdn.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.e(TAG, "FQDN is too long"); return false; } if (TextUtils.isEmpty(mCountries)) { - Log.d(TAG, "Missing countries"); + Log.e(TAG, "Missing countries"); + return false; + } + if (mCountries.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { + Log.e(TAG, "country is too long"); return false; } return true; @@ -449,7 +460,7 @@ public boolean validate() { } for (String ssid : mExcludedSsidList) { if (ssid.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { - Log.d(TAG, "Invalid SSID: " + ssid); + Log.e(TAG, "Invalid SSID: " + ssid); return false; } } @@ -457,15 +468,24 @@ public boolean validate() { // Validate required protocol to port map. if (mRequiredProtoPortMap != null) { for (Map.Entry entry : mRequiredProtoPortMap.entrySet()) { + int protocol = entry.getKey(); + if (protocol < 0 || protocol > 255) { + Log.e(TAG, "Invalid IP protocol: " + protocol); + return false; + } String portNumber = entry.getValue(); if (portNumber.getBytes(StandardCharsets.UTF_8).length > MAX_PORT_STRING_BYTES) { - Log.d(TAG, "PortNumber string bytes exceeded the max: " + portNumber); + Log.e(TAG, "PortNumber string bytes exceeded the max: " + portNumber); return false; } } } // Validate preferred roaming partner list. if (mPreferredRoamingPartnerList != null) { + if (mPreferredRoamingPartnerList.size() > MAX_NUMBER_OF_ENTRIES) { + Log.e(TAG, "Number of the Preferred Roaming Partner exceed the limit"); + return false; + } for (RoamingPartner partner : mPreferredRoamingPartnerList) { if (!partner.validate()) { return false; From c9a1e3ae779ece1ff7ab5e93006ba7ff514ee51a Mon Sep 17 00:00:00 2001 From: "Nate(Qiang) Jiang" Date: Wed, 12 Apr 2023 18:32:50 +0000 Subject: [PATCH 155/208] DO NOT MERGE: Limit the ServiceFriendlyNames Bug: 274445194 Test: atest android.net.wifi (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6b1746ab6f0ff4020c78381833554f20344c2e2a) Merged-In: Id4e16007531ba1ce3e3f9fa3d3111b5af57751be Change-Id: Id4e16007531ba1ce3e3f9fa3d3111b5af57751be --- .../wifi/hotspot2/PasspointConfiguration.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 09f91d688f7b..a6509e3e9fe1 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -842,6 +842,23 @@ private boolean validateForCommonR1andR2() { } } } + if (mServiceFriendlyNames != null) { + if (mServiceFriendlyNames.size() > MAX_NUMBER_OF_ENTRIES) { + Log.e(TAG, "ServiceFriendlyNames exceed the max!"); + return false; + } + for (Map.Entry names : mServiceFriendlyNames.entrySet()) { + if (names.getKey() == null || names.getValue() == null) { + Log.e(TAG, "Service friendly name entry should not be null"); + return false; + } + if (names.getKey().length() > MAX_STRING_LENGTH + || names.getValue().length() > MAX_STRING_LENGTH) { + Log.e(TAG, "Service friendly name is to long"); + return false; + } + } + } return true; } From 15a4329df513353d5e81331fd2b0fedd1c888dfd Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 8 Feb 2023 01:04:46 +0000 Subject: [PATCH 156/208] Only allow NEW_TASK flag when adjusting pending intents Bug: 243794108 Test: atest CtsSecurityBulletinHostTestCases:android.security.cts.CVE_2023_20918 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c62d2e1021a030f4f0ae5fcfc8fe8e0875fa669f) Merged-In: I5d329beecef1902c36704e93d0bc5cb60d0e2f5b Change-Id: I5d329beecef1902c36704e93d0bc5cb60d0e2f5b --- core/java/android/app/ActivityOptions.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index c9723bd4fce3..b8413c5aafcc 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -20,6 +20,8 @@ import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; import static android.view.Display.INVALID_DISPLAY; import android.annotation.NonNull; @@ -1372,7 +1374,9 @@ public void setPendingIntentLaunchFlags(@android.content.Intent.Flags int flags) * @hide */ public int getPendingIntentLaunchFlags() { - return mPendingIntentLaunchFlags; + // b/243794108: Ignore all flags except the new task flag, to be reconsidered in b/254490217 + return mPendingIntentLaunchFlags & + (FLAG_ACTIVITY_NEW_TASK | FLAG_RECEIVER_FOREGROUND); } /** From 59bf6c702c558459087264792393a518cf37faf9 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Tue, 28 Mar 2023 13:15:04 -0700 Subject: [PATCH 157/208] DO NOT MERGE Dismiss keyguard when simpin auth'd and... security method is none. This is mostly to fix the case where we auth sim pin in the set up wizard and it goes straight to keyguard instead of the setup wizard activity. This works with the prevent bypass keyguard flag because the device should be noe secure in this case. Fixes: 222446076 Test: turn locked sim on, which opens the sim pin screen. Auth the screen and observe that keyguard is not shown. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:48fa9bef3451e4a358c941af5b230f99881c5cb6) Cherry-picking this CL as a security fix Bug: 222446076 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:09f004722284ef6b9790ddf9338a1708b3f0833c) Merged-In: If4360dd6ae2e5f79b43eaf1a29687ac9cc4b6101 Change-Id: If4360dd6ae2e5f79b43eaf1a29687ac9cc4b6101 --- .../src/com/android/keyguard/KeyguardSecurityContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b2020d9fe2d5..1cef61a2e18a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -739,7 +739,7 @@ boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, case SimPuk: // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( + if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser())) { finish = true; eventSubtype = BOUNCER_DISMISS_SIM; From e59d9dee450d8ee6b58604e11a90dbf41d133e17 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Fri, 28 Apr 2023 14:11:04 +0000 Subject: [PATCH 158/208] DO NOT MERGE Increase notification channel limit. This was previously reduced because it can cause an out of memory error or be abused to trigger a permanent denial of service, but it breaks some messages apps in Android 11, as they are creating too many channels. Rolling it back until the apps are fixed to reduce user impact. Bug: 279447569 Bug: 261723753 Test: N/A, this was previously 50k (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5664378a7cac48184ec0702a41aaed0677d41136) Merged-In: Id8da382f812d4abb8db723c40a61366a7402da4f Change-Id: Id8da382f812d4abb8db723c40a61366a7402da4f --- .../java/com/android/server/notification/PreferencesHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 8f969dc83a09..282816079a26 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -92,7 +92,7 @@ public class PreferencesHelper implements RankingConfig { private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":"; @VisibleForTesting - static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000; + static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000; @VisibleForTesting static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 6000; From 46c91e4c836d96bcb8b664dd4facf59dc1febebe Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Fri, 21 Apr 2023 15:39:22 +0000 Subject: [PATCH 159/208] Verify URI permissions for EXTRA_REMOTE_INPUT_HISTORY_ITEMS. Also added the person URIs in the test, since they weren't being checked. Test: atest NotificationManagerServiceTest & tested with POC from bug Bug: 276729064 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e0d8602a0200ba92283463bd54cefcf97394bfa8) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:63f8ce3efd9a564ae83f1de38791a6d67c5a8ddb) Merged-In: I848545f7aee202495c515f47a32871a2cb6ae707 Change-Id: I848545f7aee202495c515f47a32871a2cb6ae707 --- core/java/android/app/Notification.java | 11 +++++++ .../NotificationManagerServiceTest.java | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 132afabe82d1..489c2e96e74b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2510,6 +2510,17 @@ public void visitUris(@NonNull Consumer visitor) { if (person != null && person.getIconUri() != null) { visitor.accept(person.getIconUri()); } + + final RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + if (history != null) { + for (int i = 0; i < history.length; i++) { + RemoteInputHistoryItem item = history[i]; + if (item.getUri() != null) { + visitor.accept(item.getUri()); + } + } + } } if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 49ad501d90e1..f6b9dba23719 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -102,6 +102,7 @@ import android.app.PendingIntent; import android.app.Person; import android.app.RemoteInput; +import android.app.RemoteInputHistoryItem; import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; @@ -4267,10 +4268,36 @@ public void updateUriPermissions_posterDoesNotOwnUri() throws Exception { public void testVisitUris() throws Exception { final Uri audioContents = Uri.parse("content://com.example/audio"); final Uri backgroundImage = Uri.parse("content://com.example/background"); + final Icon personIcon1 = Icon.createWithContentUri("content://media/person1"); + final Icon personIcon2 = Icon.createWithContentUri("content://media/person2"); + final Icon personIcon3 = Icon.createWithContentUri("content://media/person3"); + final Person person1 = new Person.Builder() + .setName("Messaging Person") + .setIcon(personIcon1) + .build(); + final Person person2 = new Person.Builder() + .setName("People List Person 1") + .setIcon(personIcon2) + .build(); + final Person person3 = new Person.Builder() + .setName("People List Person 2") + .setIcon(personIcon3) + .build(); + final Uri historyUri1 = Uri.parse("content://com.example/history1"); + final Uri historyUri2 = Uri.parse("content://com.example/history2"); + final RemoteInputHistoryItem historyItem1 = new RemoteInputHistoryItem(null, historyUri1, + "a"); + final RemoteInputHistoryItem historyItem2 = new RemoteInputHistoryItem(null, historyUri2, + "b"); Bundle extras = new Bundle(); extras.putParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI, audioContents); extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, backgroundImage.toString()); + extras.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person1); + extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, + new ArrayList<>(Arrays.asList(person2, person3))); + extras.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + new RemoteInputHistoryItem[]{historyItem1, historyItem2}); Notification n = new Notification.Builder(mContext, "a") .setContentTitle("notification with uris") @@ -4282,6 +4309,11 @@ public void testVisitUris() throws Exception { n.visitUris(visitor); verify(visitor, times(1)).accept(eq(audioContents)); verify(visitor, times(1)).accept(eq(backgroundImage)); + verify(visitor, times(1)).accept(eq(personIcon1.getUri())); + verify(visitor, times(1)).accept(eq(personIcon2.getUri())); + verify(visitor, times(1)).accept(eq(personIcon3.getUri())); + verify(visitor, times(1)).accept(eq(historyUri1)); + verify(visitor, times(1)).accept(eq(historyUri2)); } @Test From a9358d150cba2e999fed40efbeb9f8ad8334eb9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Kurucz?= Date: Fri, 21 Apr 2023 09:45:07 +0000 Subject: [PATCH 160/208] Truncate ShortcutInfo Id Creating Conversation with a ShortcutId longer than 65_535 (max unsigned short), we did not save the conversation settings into the notification_policy.xml due to a restriction in FastDataOutput. This put us to a state where the user changing the importance or turning off the notifications for the given conversation had no effect on notification behavior. Fixes: 273729476 Test: atest ShortcutManagerTest2 Test: Create a test app which creates a Conversation with a long shortcutId. Go to the Conversation Settings and turn off Notifications. Post a new Notification to this Conversation and see if it is displayed. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f31df6234091b5b1de258a01dd4b2d8e5415ee2e) Merged-In: I2617de6f9e8a7dbfd8fbeff589a7d592f00d87c5 Change-Id: I2617de6f9e8a7dbfd8fbeff589a7d592f00d87c5 --- .../java/android/content/pm/ShortcutInfo.java | 20 ++++++++++++++++--- .../server/pm/ShortcutManagerTest2.java | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 1b3c46f90851..bf1219ccd872 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -261,6 +261,12 @@ public final class ShortcutInfo implements Parcelable { */ public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103; + /** + * The maximum length of Shortcut ID. IDs will be truncated at this limit. + * @hide + */ + public static final int MAX_ID_LENGTH = 1000; + /** @hide */ @IntDef(prefix = { "DISABLED_REASON_" }, value = { DISABLED_REASON_NOT_DISABLED, @@ -436,8 +442,7 @@ public static boolean isDisabledForRestoreIssue(@DisabledReason int disabledReas private ShortcutInfo(Builder b) { mUserId = b.mContext.getUserId(); - - mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided"); + mId = getSafeId(Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided")); // Note we can't do other null checks here because SM.updateShortcuts() takes partial // information. @@ -539,6 +544,14 @@ private static Person[] clonePersons(Person[] persons) { return ret; } + @NonNull + private static String getSafeId(@NonNull String id) { + if (id.length() > MAX_ID_LENGTH) { + return id.substring(0, MAX_ID_LENGTH); + } + return id; + } + /** * Throws if any of the mandatory fields is not set. * @@ -2090,7 +2103,8 @@ private ShortcutInfo(Parcel source) { final ClassLoader cl = getClass().getClassLoader(); mUserId = source.readInt(); - mId = source.readString8(); + mId = getSafeId(Preconditions.checkStringNotEmpty(source.readString8(), + "Shortcut ID must be provided")); mPackageName = source.readString8(); mActivity = source.readParcelable(cl); mFlags = source.readInt(); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index ca7704950b51..088d693205c2 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -53,6 +53,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; +import java.util.Collections; import java.util.Locale; /** @@ -223,6 +224,15 @@ public void testShortcutInfoMissingMandatoryFields() { }); } + public void testShortcutIdTruncated() { + ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), + String.join("", Collections.nCopies(Short.MAX_VALUE, "s"))).build(); + + assertTrue( + "id must be truncated to MAX_ID_LENGTH", + si.getId().length() <= ShortcutInfo.MAX_ID_LENGTH); + } + public void testShortcutInfoParcel() { setCaller(CALLING_PACKAGE_1, USER_10); ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext) From 52986485a5f5a3d6ea963ee847932263640149d3 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 27 Apr 2023 12:36:05 +0000 Subject: [PATCH 161/208] Visit URIs in landscape/portrait custom remote views. Bug: 277740848 Test: atest RemoteViewsTest NotificationManagerServiceTest & tested with POC from bug (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e8acb2f660bdb03616989852f9dbbf1726f8237e) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:43e1ae4e0d408604b9e3c18ac0e9bf87529b92a8) Merged-In: I7d3d35df0ec38945019f71755bed8797b7af4517 Change-Id: I7d3d35df0ec38945019f71755bed8797b7af4517 --- core/java/android/widget/RemoteViews.java | 6 ++ .../src/android/widget/RemoteViewsTest.java | 64 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index a9b2c4df255f..ec36b9ed578c 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -562,6 +562,12 @@ public void visitUris(@NonNull Consumer visitor) { mActions.get(i).visitUris(visitor); } } + if (mLandscape != null) { + mLandscape.visitUris(visitor); + } + if (mPortrait != null) { + mPortrait.visitUris(visitor); + } } private static void visitIconUri(Icon icon, @NonNull Consumer visitor) { diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 8cb7e1b95245..46f2c0928fc3 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -20,6 +20,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import android.app.ActivityOptions; import android.app.PendingIntent; @@ -29,6 +33,8 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; import android.os.Parcel; @@ -50,6 +56,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; /** * Tests for RemoteViews. @@ -499,4 +506,61 @@ public ActivityOptions createSharedElementActivityOptions( return null; } } + + @Test + public void visitUris() { + RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); + + final Uri imageUri = Uri.parse("content://media/image"); + final Icon icon1 = Icon.createWithContentUri("content://media/icon1"); + final Icon icon2 = Icon.createWithContentUri("content://media/icon2"); + final Icon icon3 = Icon.createWithContentUri("content://media/icon3"); + final Icon icon4 = Icon.createWithContentUri("content://media/icon4"); + views.setImageViewUri(R.id.image, imageUri); + views.setTextViewCompoundDrawables(R.id.text, icon1, icon2, icon3, icon4); + + Consumer visitor = (Consumer) spy(Consumer.class); + views.visitUris(visitor); + verify(visitor, times(1)).accept(eq(imageUri)); + verify(visitor, times(1)).accept(eq(icon1.getUri())); + verify(visitor, times(1)).accept(eq(icon2.getUri())); + verify(visitor, times(1)).accept(eq(icon3.getUri())); + verify(visitor, times(1)).accept(eq(icon4.getUri())); + } + + @Test + public void visitUris_separateOrientation() { + final RemoteViews landscape = new RemoteViews(mPackage, R.layout.remote_views_test); + final Uri imageUriL = Uri.parse("content://landscape/image"); + final Icon icon1L = Icon.createWithContentUri("content://landscape/icon1"); + final Icon icon2L = Icon.createWithContentUri("content://landscape/icon2"); + final Icon icon3L = Icon.createWithContentUri("content://landscape/icon3"); + final Icon icon4L = Icon.createWithContentUri("content://landscape/icon4"); + landscape.setImageViewUri(R.id.image, imageUriL); + landscape.setTextViewCompoundDrawables(R.id.text, icon1L, icon2L, icon3L, icon4L); + + final RemoteViews portrait = new RemoteViews(mPackage, 33); + final Uri imageUriP = Uri.parse("content://portrait/image"); + final Icon icon1P = Icon.createWithContentUri("content://portrait/icon1"); + final Icon icon2P = Icon.createWithContentUri("content://portrait/icon2"); + final Icon icon3P = Icon.createWithContentUri("content://portrait/icon3"); + final Icon icon4P = Icon.createWithContentUri("content://portrait/icon4"); + portrait.setImageViewUri(R.id.image, imageUriP); + portrait.setTextViewCompoundDrawables(R.id.text, icon1P, icon2P, icon3P, icon4P); + + RemoteViews views = new RemoteViews(landscape, portrait); + + Consumer visitor = (Consumer) spy(Consumer.class); + views.visitUris(visitor); + verify(visitor, times(1)).accept(eq(imageUriL)); + verify(visitor, times(1)).accept(eq(icon1L.getUri())); + verify(visitor, times(1)).accept(eq(icon2L.getUri())); + verify(visitor, times(1)).accept(eq(icon3L.getUri())); + verify(visitor, times(1)).accept(eq(icon4L.getUri())); + verify(visitor, times(1)).accept(eq(imageUriP)); + verify(visitor, times(1)).accept(eq(icon1P.getUri())); + verify(visitor, times(1)).accept(eq(icon2P.getUri())); + verify(visitor, times(1)).accept(eq(icon3P.getUri())); + verify(visitor, times(1)).accept(eq(icon4P.getUri())); + } } From 0a1d6d07aaa15affdedd312e8aadadc67041ee94 Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Tue, 25 Oct 2022 22:39:52 -0700 Subject: [PATCH 162/208] DO NOT MERGE: ActivityManager#killBackgroundProcesses can kill caller's own app only unless it's a system app. Bug: 239423414 Bug: 223376078 Test: atest CtsAppTestCases:ActivityManagerTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8b382775b258220466a977453905797521e159de) Merged-In: Iac6baa889965b8ffecd9a43179a4c96632ad1d02 Change-Id: Iac6baa889965b8ffecd9a43179a4c96632ad1d02 --- core/java/android/app/ActivityManager.java | 3 ++ core/res/AndroidManifest.xml | 6 +++- .../server/am/ActivityManagerService.java | 32 +++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1059a6f2e868..811cd5b75275 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3933,6 +3933,9 @@ public void restartPackage(String packageName) { * processes to reclaim memory; the system will take care of restarting * these processes in the future as needed. * + *

Third party applications can only use this API to kill their own processes. + *

+ * * @param packageName The name of the package whose processes are to * be killed. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ea1ea9f9ed98..0fdc0851bcc7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2549,7 +2549,11 @@ android:protectionLevel="normal" /> = FIRST_APPLICATION_UID + && (proc == null || !proc.info.isSystemApp())) { + final String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + + callingPid + ", uid=" + callingUid + " is not allowed"; + Slog.w(TAG, msg); + // Silently return to avoid existing apps from crashing. + return; + } + final long callingId = Binder.clearCallingIdentity(); try { synchronized (this) { From 58755c09161559fe2796092db6a9d40a8e65e127 Mon Sep 17 00:00:00 2001 From: Austin Borger Date: Sat, 18 Mar 2023 12:56:12 -0700 Subject: [PATCH 163/208] ActivityManagerService: Allow openContentUri from vendor/system/product. Apps should not have direct access to this entry point. Check that the caller is a vendor, system, or product package. Test: Ran PoC app and CtsMediaPlayerTestCases. Bug: 236688380 (cherry picked from commit d0ba7467c2cb2815f94f6651cbb1c2f405e8e9c7) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:821f4c0d8ba06be32ce9b46c7a7c09d1cacd7b0e) Merged-In: I0335496d28fa5fc3bfe1fecd4be90040b0b3687f Change-Id: I0335496d28fa5fc3bfe1fecd4be90040b0b3687f --- .../server/am/ActivityManagerService.java | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8e9f59a51185..1cd202dce438 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -162,6 +162,7 @@ import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.app.ApplicationThreadConstants; +import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.app.ContentProviderHolder; import android.app.IActivityController; @@ -364,6 +365,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.uri.GrantUri; import com.android.server.uri.NeededUriGrants; @@ -8329,7 +8331,54 @@ public ParcelFileDescriptor openContentUri(String uriString) throws RemoteExcept Binder token = new Binder(); sCallerIdentity.set(new Identity( token, Binder.getCallingPid(), Binder.getCallingUid())); + boolean handlingSecurityViolation = false; try { + // This method is exposed to the VNDK and to avoid changing its + // signature we just use the first package in the UID. For shared + // UIDs we may blame the wrong app but that is Okay as they are + // in the same security/privacy sandbox. + final int uid = Binder.getCallingUid(); + // Here we handle some of the special UIDs (mediaserver, systemserver, etc) + // Note: This is moved to AppOpsManager.resolvePackageName in future versions. + final String packageName; + if (uid == Process.ROOT_UID) { + packageName = "root"; + } else if (uid == Process.SHELL_UID) { + packageName = "com.android.shell"; + } else if (uid == Process.MEDIA_UID) { + packageName = "media"; + } else if (uid == Process.AUDIOSERVER_UID) { + packageName = "audioserver"; + } else if (uid == Process.CAMERASERVER_UID) { + packageName = "cameraserver"; + } else if (uid == Process.SYSTEM_UID) { + packageName = "android"; + } else { + packageName = null; + } + + final AndroidPackage androidPackage; + if (packageName != null) { + androidPackage = mPackageManagerInt.getPackage(packageName); + } else { + androidPackage = mPackageManagerInt.getPackage(uid); + } + if (androidPackage == null) { + Log.e(TAG, "Cannot find package for uid: " + uid); + handlingSecurityViolation = true; + return null; + } + + final ApplicationInfo appInfo = mPackageManagerInt.getApplicationInfo( + androidPackage.getPackageName(), /*flags*/0, Process.SYSTEM_UID, + UserHandle.USER_SYSTEM); + if (!appInfo.isVendor() && !appInfo.isSystemApp() && !appInfo.isSystemExt() + && !appInfo.isProduct()) { + Log.e(TAG, "openContentUri may only be used by vendor/system/product."); + handlingSecurityViolation = true; + return null; + } + pfd = cph.provider.openFile(null, null, uri, "r", null, token); } catch (FileNotFoundException e) { // do nothing; pfd will be returned null @@ -8337,7 +8386,16 @@ public ParcelFileDescriptor openContentUri(String uriString) throws RemoteExcept // Ensure that whatever happens, we clean up the identity state sCallerIdentity.remove(); // Ensure we're done with the provider. - removeContentProviderExternalUnchecked(name, null, userId); + try { + removeContentProviderExternalUnchecked(name, null, userId); + } catch (SecurityException e) { + // A SecurityException may be thrown from computeOomAdjLocked if the calling + // UID is that of a malicious app accessing this hidden API. In that case + // we're already handling that by returning null, so tolerate this. + if (!handlingSecurityViolation) { + throw e; + } + } } } else { Slog.d(TAG, "Failed to get provider for authority '" + name + "'"); From e2ae7ba2a1c82edea3225eba03707bfbfface71a Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 27 Apr 2023 14:55:28 +0000 Subject: [PATCH 164/208] Verify URI permissions for notification shortcutIcon. Bug: 277593270 Test: atest NotificationManagerServiceTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:47e661cbf37e1dedf676f482ac07ffc433c92d0b) Merged-In: I1efaa1301bca36895ad4322a919d7421156a60df Change-Id: I1efaa1301bca36895ad4322a919d7421156a60df --- core/java/android/app/Notification.java | 29 ++++++++--- .../NotificationManagerServiceTest.java | 51 ++++++++++++++++++- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 489c2e96e74b..a1383f5c6e49 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2476,6 +2476,14 @@ public void cloneInto(Notification that, boolean heavy) { } } + private static void visitIconUri(@NonNull Consumer visitor, @Nullable Icon icon) { + if (icon == null) return; + final int iconType = icon.getType(); + if (iconType == TYPE_URI || iconType == TYPE_URI_ADAPTIVE_BITMAP) { + visitor.accept(icon.getUri()); + } + } + /** * Note all {@link Uri} that are referenced internally, with the expectation * that Uri permission grants will need to be issued to ensure the recipient @@ -2491,7 +2499,18 @@ public void visitUris(@NonNull Consumer visitor) { if (bigContentView != null) bigContentView.visitUris(visitor); if (headsUpContentView != null) headsUpContentView.visitUris(visitor); + visitIconUri(visitor, mSmallIcon); + visitIconUri(visitor, mLargeIcon); + + if (actions != null) { + for (Action action : actions) { + visitIconUri(visitor, action.getIcon()); + } + } + if (extras != null) { + visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG)); + visitor.accept(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI)); if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) { visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI))); @@ -2549,14 +2568,12 @@ public void visitUris(@NonNull Consumer visitor) { } } } + + visitIconUri(visitor, extras.getParcelable(EXTRA_CONVERSATION_ICON)); } - if (mBubbleMetadata != null && mBubbleMetadata.getIcon() != null) { - final Icon icon = mBubbleMetadata.getIcon(); - final int iconType = icon.getType(); - if (iconType == TYPE_URI_ADAPTIVE_BITMAP || iconType == TYPE_URI) { - visitor.accept(icon.getUri()); - } + if (mBubbleMetadata != null) { + visitIconUri(visitor, mBubbleMetadata.getIcon()); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index f6b9dba23719..109c6ff6e6fc 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4268,6 +4268,8 @@ public void updateUriPermissions_posterDoesNotOwnUri() throws Exception { public void testVisitUris() throws Exception { final Uri audioContents = Uri.parse("content://com.example/audio"); final Uri backgroundImage = Uri.parse("content://com.example/background"); + final Icon smallIcon = Icon.createWithContentUri("content://media/small/icon"); + final Icon largeIcon = Icon.createWithContentUri("content://media/large/icon"); final Icon personIcon1 = Icon.createWithContentUri("content://media/person1"); final Icon personIcon2 = Icon.createWithContentUri("content://media/person2"); final Icon personIcon3 = Icon.createWithContentUri("content://media/person3"); @@ -4301,7 +4303,8 @@ public void testVisitUris() throws Exception { Notification n = new Notification.Builder(mContext, "a") .setContentTitle("notification with uris") - .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setSmallIcon(smallIcon) + .setLargeIcon(largeIcon) .addExtras(extras) .build(); @@ -4309,6 +4312,8 @@ public void testVisitUris() throws Exception { n.visitUris(visitor); verify(visitor, times(1)).accept(eq(audioContents)); verify(visitor, times(1)).accept(eq(backgroundImage)); + verify(visitor, times(1)).accept(eq(smallIcon.getUri())); + verify(visitor, times(1)).accept(eq(largeIcon.getUri())); verify(visitor, times(1)).accept(eq(personIcon1.getUri())); verify(visitor, times(1)).accept(eq(personIcon2.getUri())); verify(visitor, times(1)).accept(eq(personIcon3.getUri())); @@ -4316,6 +4321,50 @@ public void testVisitUris() throws Exception { verify(visitor, times(1)).accept(eq(historyUri2)); } + @Test + public void testVisitUris_messagingStyle() { + final Icon personIcon1 = Icon.createWithContentUri("content://media/person1"); + final Icon personIcon2 = Icon.createWithContentUri("content://media/person2"); + final Icon personIcon3 = Icon.createWithContentUri("content://media/person3"); + final Person person1 = new Person.Builder() + .setName("Messaging Person 1") + .setIcon(personIcon1) + .build(); + final Person person2 = new Person.Builder() + .setName("Messaging Person 2") + .setIcon(personIcon2) + .build(); + final Person person3 = new Person.Builder() + .setName("Messaging Person 3") + .setIcon(personIcon3) + .build(); + Icon shortcutIcon = Icon.createWithContentUri("content://media/shortcut"); + + Notification.Builder builder = new Notification.Builder(mContext, "a") + .setCategory(Notification.CATEGORY_MESSAGE) + .setContentTitle("new message!") + .setContentText("Conversation Notification") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + Notification.MessagingStyle.Message message1 = new Notification.MessagingStyle.Message( + "Marco?", System.currentTimeMillis(), person2); + Notification.MessagingStyle.Message message2 = new Notification.MessagingStyle.Message( + "Polo!", System.currentTimeMillis(), person3); + Notification.MessagingStyle style = new Notification.MessagingStyle(person1) + .addMessage(message1) + .addMessage(message2) + .setShortcutIcon(shortcutIcon); + builder.setStyle(style); + Notification n = builder.build(); + + Consumer visitor = (Consumer) spy(Consumer.class); + n.visitUris(visitor); + + verify(visitor, times(1)).accept(eq(shortcutIcon.getUri())); + verify(visitor, times(1)).accept(eq(personIcon1.getUri())); + verify(visitor, times(1)).accept(eq(personIcon2.getUri())); + verify(visitor, times(1)).accept(eq(personIcon3.getUri())); + } + @Test public void testSetNotificationPolicy_preP_setOldFields() { ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); From fc2177eb890d7560ffbaeaa64f6dffec158bfb08 Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 8 May 2023 16:33:12 +0000 Subject: [PATCH 165/208] On device lockdown, always show the keyguard Manual test steps: 1. Enable app pinning and disable "Ask for PIN before unpinning" setting 2. Pin an app (ie: Settings) 3. Lockdown from the power menu Observe: user is brought to the keyguard, primary auth is required to enter the device. After entering credential, the device is still in app pinning mode. Test: atest KeyguardViewMediatorTest Test: manual steps outlined above Bug: 218495634 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b23c2d5fb6630ea0da503b937f62880594b13e94) Merged-In: I9a7c5e1acadabd4484e58573331f98dba895f2a2 Change-Id: I9a7c5e1acadabd4484e58573331f98dba895f2a2 --- .../systemui/keyguard/KeyguardViewMediator.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index bef47fe6a374..dbb94f813d93 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -584,6 +584,13 @@ public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { notifyHasLockscreenWallpaperChanged(hasLockscreenWallpaper); } } + + @Override + public void onStrongAuthStateChanged(int userId) { + if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + doKeyguardLocked(null); + } + } }; ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() { @@ -1400,7 +1407,8 @@ private void doKeyguardLocked(Bundle options) { } // if another app is disabling us, don't show - if (!mExternallyEnabled) { + if (!mExternallyEnabled + && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); mNeedToReshowWhenReenabled = true; From 540d3e63c6f63bef2841b3b63aa2f13e54f84cf1 Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Wed, 5 Apr 2023 15:15:41 +0000 Subject: [PATCH 166/208] Ensure policy has no absurdly long strings The following APIs now enforce limits and throw IllegalArgumentException when limits are violated: * DPM.setTrustAgentConfiguration() limits agent packgage name, component name, and strings within configuration bundle. * DPM.setPermittedAccessibilityServices() limits package names. * DPM.setPermittedInputMethods() limits package names. * DPM.setAccountManagementDisabled() limits account name. * DPM.setLockTaskPackages() limits package names. * DPM.setAffiliationIds() limits id. * DPM.transferOwnership() limits strings inside the bundle. Package names are limited at 223, because they become directory names and it is a filesystem restriction, see FrameworkParsingPackageUtils. All other strings are limited at 65535, because longer ones break binary XML serializer. The following APIs silently truncate strings that are long beyond reason: * DPM.setShortSupportMessage() truncates message at 200. * DPM.setLongSupportMessage() truncates message at 20000. * DPM.setOrganizationName() truncates org name at 200. Bug: 260729089 Test: atest com.android.server.devicepolicy (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bb7e82ceaa6d16267e7b0e14563161b506d26be8) Merged-In: Idcf54e408722f164d16bf2f24a00cd1f5b626d23 Change-Id: Idcf54e408722f164d16bf2f24a00cd1f5b626d23 --- .../app/admin/DevicePolicyManager.java | 3 +- .../DevicePolicyManagerService.java | 92 ++++++++++++++++++- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 67d534b6fc69..9f0675617d5e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9907,7 +9907,8 @@ public CharSequence getShortSupportMessage(@NonNull ComponentName admin) { /** * Called by a device admin to set the long support message. This will be displayed to the user - * in the device administators settings screen. + * in the device administrators settings screen. If the message is longer than 20000 characters + * it may be truncated. *

* If the long support message needs to be localized, it is the responsibility of the * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b82888a53c6e..5835cc53e854 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -321,6 +321,7 @@ import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.time.LocalDate; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -332,6 +333,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Queue; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -405,6 +407,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final int REQUEST_PROFILE_OFF_DEADLINE = 5572; + // Binary XML serializer doesn't support longer strings + private static final int MAX_POLICY_STRING_LENGTH = 65535; + // FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE, Android packages are used in dir names. + private static final int MAX_PACKAGE_NAME_LENGTH = 223; + + private static final int MAX_LONG_SUPPORT_MESSAGE_LENGTH = 20000; + private static final int MAX_SHORT_SUPPORT_MESSAGE_LENGTH = 200; + private static final int MAX_ORG_NAME_LENGTH = 200; + private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1); private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms @@ -10123,6 +10134,12 @@ public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent, } Objects.requireNonNull(admin, "admin is null"); Objects.requireNonNull(agent, "agent is null"); + enforceMaxPackageNameLength(agent.getPackageName()); + final String agentAsString = agent.flattenToString(); + enforceMaxStringLength(agentAsString, "agent name"); + if (args != null) { + enforceMaxStringLength(args, "args"); + } final int userHandle = UserHandle.getCallingUserId(); synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked(admin, @@ -10341,6 +10358,10 @@ public boolean setPermittedAccessibilityServices(ComponentName who, List package Objects.requireNonNull(who, "ComponentName is null"); if (packageList != null) { + for (String pkg : (List) packageList) { + enforceMaxPackageNameLength(pkg); + } + int userId = UserHandle.getCallingUserId(); List enabledServices = null; long id = mInjector.binderClearCallingIdentity(); @@ -10492,6 +10513,10 @@ public boolean setPermittedInputMethods(ComponentName who, List packageList) { Objects.requireNonNull(who, "ComponentName is null"); final int callingUserId = mInjector.userHandleGetCallingUserId(); if (packageList != null) { + for (String pkg : (List) packageList) { + enforceMaxPackageNameLength(pkg); + } + List enabledImes = InputMethodManagerInternal.get() .getEnabledInputMethodListAsUser(callingUserId); if (enabledImes != null) { @@ -11494,6 +11519,8 @@ public void setAccountManagementDisabled(ComponentName who, String accountType, return; } Objects.requireNonNull(who, "ComponentName is null"); + enforceMaxStringLength(accountType, "account type"); + synchronized (getLockObject()) { /* * When called on the parent DPM instance (parent == true), affects active admin @@ -11840,6 +11867,9 @@ public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packages, "packages is null"); + for (String pkg : packages) { + enforceMaxPackageNameLength(pkg); + } synchronized (getLockObject()) { enforceCanCallLockTaskLocked(who); @@ -13685,6 +13715,8 @@ public void setShortSupportMessage(@NonNull ComponentName who, CharSequence mess return; } Objects.requireNonNull(who, "ComponentName is null"); + message = truncateIfLonger(message, MAX_SHORT_SUPPORT_MESSAGE_LENGTH); + final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); @@ -13716,6 +13748,9 @@ public void setLongSupportMessage(@NonNull ComponentName who, CharSequence messa if (!mHasFeature) { return; } + + message = truncateIfLonger(message, MAX_LONG_SUPPORT_MESSAGE_LENGTH); + Objects.requireNonNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { @@ -13849,6 +13884,8 @@ public void setOrganizationName(@NonNull ComponentName who, CharSequence text) { Objects.requireNonNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); + text = truncateIfLonger(text, MAX_ORG_NAME_LENGTH); + synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); @@ -14087,9 +14124,8 @@ public void setAffiliationIds(ComponentName admin, List ids) { throw new IllegalArgumentException("ids must not be null"); } for (String id : ids) { - if (TextUtils.isEmpty(id)) { - throw new IllegalArgumentException("ids must not contain empty string"); - } + Preconditions.checkArgument(!TextUtils.isEmpty(id), "ids must not have empty string"); + enforceMaxStringLength(id, "affiliation id"); } final Set affiliationIds = new ArraySet<>(ids); @@ -15216,6 +15252,9 @@ public void transferOwnership(@NonNull ComponentName admin, @NonNull ComponentNa Objects.requireNonNull(admin, "Admin cannot be null."); Objects.requireNonNull(target, "Target cannot be null."); + if (bundle != null) { + enforceMaxStringLength(bundle, "bundle"); + } enforceProfileOrDeviceOwner(admin); @@ -16502,4 +16541,51 @@ public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return true; } } + + /** + * Truncates char sequence to maximum length, nulls are ignored. + */ + private static CharSequence truncateIfLonger(CharSequence input, int maxLength) { + return input == null || input.length() <= maxLength + ? input + : input.subSequence(0, maxLength); + } + + /** + * Throw if string argument is too long to be serialized. + */ + private static void enforceMaxStringLength(String str, String argName) { + Preconditions.checkArgument( + str.length() <= MAX_POLICY_STRING_LENGTH, argName + " loo long"); + } + + private static void enforceMaxPackageNameLength(String pkg) { + Preconditions.checkArgument( + pkg.length() <= MAX_PACKAGE_NAME_LENGTH, "Package name too long"); + } + + /** + * Throw if persistable bundle contains any string that we can't serialize. + */ + private static void enforceMaxStringLength(PersistableBundle bundle, String argName) { + // Persistable bundles can have other persistable bundles as values, traverse with a queue. + Queue queue = new ArrayDeque<>(); + queue.add(bundle); + while (!queue.isEmpty()) { + PersistableBundle current = queue.remove(); + for (String key : current.keySet()) { + enforceMaxStringLength(key, "key in " + argName); + Object value = current.get(key); + if (value instanceof String) { + enforceMaxStringLength((String) value, "string value in " + argName); + } else if (value instanceof String[]) { + for (String str : (String[]) value) { + enforceMaxStringLength(str, "string value in " + argName); + } + } else if (value instanceof PersistableBundle) { + queue.add((PersistableBundle) value); + } + } + } + } } From 5429fbcac0c055543eaefc78bce2dc5b14493273 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Fri, 12 May 2023 15:41:09 +0000 Subject: [PATCH 167/208] Implement visitUris for RemoteViews ViewGroupActionAdd. This is to prevent a vulnerability where notifications can show resources belonging to other users, since the URI in the nested views was not being checked. Bug: 277740082 Test: atest RemoteViewsTest NotificationVisitUrisTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:850fd984e5f346645b5a941ed7307387c7e4c4de) Merged-In: I5c71f0bad0a6f6361eb5ceffe8d1e47e936d78f8 Change-Id: I5c71f0bad0a6f6361eb5ceffe8d1e47e936d78f8 --- core/java/android/widget/RemoteViews.java | 5 ++++ .../src/android/widget/RemoteViewsTest.java | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index ec36b9ed578c..6693b937a6e5 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1680,6 +1680,11 @@ public boolean prefersAsyncApply() { public int getActionTag() { return VIEW_GROUP_ACTION_ADD_TAG; } + + @Override + public final void visitUris(@NonNull Consumer visitor) { + mNestedViews.visitUris(visitor); + } } /** diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 46f2c0928fc3..83ff725b5b75 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -528,6 +528,30 @@ public void visitUris() { verify(visitor, times(1)).accept(eq(icon4.getUri())); } + @Test + public void visitUris_nestedViews() { + final RemoteViews outer = new RemoteViews(mPackage, R.layout.remote_views_test); + + final RemoteViews inner = new RemoteViews(mPackage, 33); + final Uri imageUriI = Uri.parse("content://inner/image"); + final Icon icon1 = Icon.createWithContentUri("content://inner/icon1"); + final Icon icon2 = Icon.createWithContentUri("content://inner/icon2"); + final Icon icon3 = Icon.createWithContentUri("content://inner/icon3"); + final Icon icon4 = Icon.createWithContentUri("content://inner/icon4"); + inner.setImageViewUri(R.id.image, imageUriI); + inner.setTextViewCompoundDrawables(R.id.text, icon1, icon2, icon3, icon4); + + outer.addView(R.id.layout, inner); + + Consumer visitor = (Consumer) spy(Consumer.class); + outer.visitUris(visitor); + verify(visitor, times(1)).accept(eq(imageUriI)); + verify(visitor, times(1)).accept(eq(icon1.getUri())); + verify(visitor, times(1)).accept(eq(icon2.getUri())); + verify(visitor, times(1)).accept(eq(icon3.getUri())); + verify(visitor, times(1)).accept(eq(icon4.getUri())); + } + @Test public void visitUris_separateOrientation() { final RemoteViews landscape = new RemoteViews(mPackage, R.layout.remote_views_test); From 734bbf06cb56e83504aa5d2ec734dbe54afcd0d5 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Mon, 15 May 2023 16:15:55 +0000 Subject: [PATCH 168/208] Check URIs in notification public version. Bug: 276294099 Test: atest NotificationManagerServiceTest NotificationVisitUrisTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9663d493142b59c65311bc09d48427d3bdde0222) Merged-In: I670198b213abb2cb29a9865eb9d1e897700508b4 Change-Id: I670198b213abb2cb29a9865eb9d1e897700508b4 --- core/java/android/app/Notification.java | 4 ++++ .../NotificationManagerServiceTest.java | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a1383f5c6e49..abc50300ca68 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2492,6 +2492,10 @@ private static void visitIconUri(@NonNull Consumer visitor, @Nullable Icon * @hide */ public void visitUris(@NonNull Consumer visitor) { + if (publicVersion != null) { + publicVersion.visitUris(visitor); + } + visitor.accept(sound); if (tickerView != null) tickerView.visitUris(visitor); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 109c6ff6e6fc..e5bea2bfaa59 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4321,6 +4321,26 @@ public void testVisitUris() throws Exception { verify(visitor, times(1)).accept(eq(historyUri2)); } + @Test + public void testVisitUris_publicVersion() throws Exception { + final Icon smallIconPublic = Icon.createWithContentUri("content://media/small/icon"); + final Icon largeIconPrivate = Icon.createWithContentUri("content://media/large/icon"); + + Notification publicVersion = new Notification.Builder(mContext, "a") + .setContentTitle("notification with uris") + .setSmallIcon(smallIconPublic) + .build(); + Notification n = new Notification.Builder(mContext, "a") + .setLargeIcon(largeIconPrivate) + .setPublicVersion(publicVersion) + .build(); + + Consumer visitor = (Consumer) spy(Consumer.class); + n.visitUris(visitor); + verify(visitor, times(1)).accept(eq(smallIconPublic.getUri())); + verify(visitor, times(1)).accept(eq(largeIconPrivate.getUri())); + } + @Test public void testVisitUris_messagingStyle() { final Icon personIcon1 = Icon.createWithContentUri("content://media/person1"); From 22a5ddf4f6ad57edaf3d865519fb64d05e2a5d69 Mon Sep 17 00:00:00 2001 From: Michael Mikhail Date: Fri, 28 Apr 2023 16:17:16 +0000 Subject: [PATCH 169/208] Verify URI permissions in MediaMetadata Add a check for URI permission to make sure that user can access the URI set in MediaMetadata. If permission is denied, clear the URI string set in metadata. Bug: 271851153 Test: atest MediaSessionTest Test: Verified by POC app attached in bug, image of second user is not the UMO background of the first user. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f95b7fc61d6b3bf49420ded0357bec031f8cbdcf) Merged-In: I384f8e230c909d8fc8e5f147e2fd3558fec44626 Change-Id: I384f8e230c909d8fc8e5f147e2fd3558fec44626 --- .../server/media/MediaSessionRecord.java | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 348e9c122a9a..5ea5d94120f3 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -18,6 +18,8 @@ import android.annotation.Nullable; import android.app.PendingIntent; +import android.content.ContentProvider; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -49,11 +51,13 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.view.KeyEvent; import com.android.server.LocalServices; +import com.android.server.uri.UriGrantsManagerInternal; import java.io.PrintWriter; import java.util.ArrayList; @@ -67,6 +71,10 @@ // TODO(jaewan): Do not call service method directly -- introduce listener instead. public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl { private static final String TAG = "MediaSessionRecord"; + private static final String[] ART_URIS = new String[] { + MediaMetadata.METADATA_KEY_ALBUM_ART_URI, + MediaMetadata.METADATA_KEY_ART_URI, + MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI}; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** @@ -120,6 +128,7 @@ private static int getVolumeStream(@Nullable AudioAttributes attr) { private final SessionStub mSession; private final SessionCb mSessionCb; private final MediaSessionService mService; + private final UriGrantsManagerInternal mUgmInternal; private final Context mContext; private final Object mLock = new Object(); @@ -182,6 +191,7 @@ public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPa mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mAudioAttrs = DEFAULT_ATTRIBUTES; mPolicies = policies; + mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); // May throw RemoteException if the session app is killed. mSessionCb.mCb.asBinder().linkToDeath(this, 0); @@ -859,21 +869,45 @@ public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException { public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) throws RemoteException { synchronized (mLock) { - MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata) - .build(); - // This is to guarantee that the underlying bundle is unparceled - // before we set it to prevent concurrent reads from throwing an - // exception - if (temp != null) { - temp.size(); - } - mMetadata = temp; mDuration = duration; mMetadataDescription = metadataDescription; + mMetadata = sanitizeMediaMetadata(metadata); } mHandler.post(MessageHandler.MSG_UPDATE_METADATA); } + private MediaMetadata sanitizeMediaMetadata(MediaMetadata metadata) { + if (metadata == null) { + return null; + } + MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(metadata); + for (String key: ART_URIS) { + String uriString = metadata.getString(key); + if (TextUtils.isEmpty(uriString)) { + continue; + } + Uri uri = Uri.parse(uriString); + if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + continue; + } + try { + mUgmInternal.checkGrantUriPermission(getUid(), + getPackageName(), + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, getUserId())); + } catch (SecurityException e) { + metadataBuilder.putString(key, null); + } + } + MediaMetadata sanitizedMetadata = metadataBuilder.build(); + // sanitizedMetadata.size() guarantees that the underlying bundle is unparceled + // before we set it to prevent concurrent reads from throwing an + // exception + sanitizedMetadata.size(); + return sanitizedMetadata; + } + @Override public void setPlaybackState(PlaybackState state) throws RemoteException { int oldState = mPlaybackState == null From 8aea0d81b3431bb6ef678dd90147e386e64e9bb8 Mon Sep 17 00:00:00 2001 From: Chandru S Date: Tue, 16 May 2023 10:41:07 -0700 Subject: [PATCH 170/208] Use Settings.System.getIntForUser instead of getInt to make sure user specific settings are used Bug: 265431505 Test: atest KeyguardViewMediatorTest (cherry picked from commit 625e009fc195ba5d658ca2d78ebb23d2770cc6c4) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ce6510deba06bcb72a0e468294b483fc4ac4be17) Merged-In: I66a660c091c90a957a0fd1e144c013840db3f47e Change-Id: I66a660c091c90a957a0fd1e144c013840db3f47e --- .../systemui/keyguard/KeyguardViewMediator.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index dbb94f813d93..d0229baa4108 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -993,9 +993,9 @@ private long getLockTimeout(int userId) { final ContentResolver cr = mContext.getContentResolver(); // From SecuritySettings - final long lockAfterTimeout = Settings.Secure.getInt(cr, + final long lockAfterTimeout = Settings.Secure.getIntForUser(cr, Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, - KEYGUARD_LOCK_AFTER_DELAY_DEFAULT); + KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, userId); // From DevicePolicyAdmin final long policyTimeout = mLockPatternUtils.getDevicePolicyManager() @@ -1007,8 +1007,8 @@ private long getLockTimeout(int userId) { timeout = lockAfterTimeout; } else { // From DisplaySettings - long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT, - KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT); + long displayTimeout = Settings.System.getIntForUser(cr, SCREEN_OFF_TIMEOUT, + KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT, userId); // policy in effect. Make sure we don't go beyond policy limit. displayTimeout = Math.max(displayTimeout, 0); // ignore negative values @@ -1855,7 +1855,10 @@ private void playSounds(boolean locked) { private void playSound(int soundId) { if (soundId == 0) return; final ContentResolver cr = mContext.getContentResolver(); - if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) { + int lockscreenSoundsEnabled = Settings.System.getIntForUser(cr, + Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1, + KeyguardUpdateMonitor.getCurrentUser()); + if (lockscreenSoundsEnabled == 1) { mLockSounds.stop(mLockSoundStreamId); // Init mAudioManager From ea7a0105b3a1dd474ca1a2352fdd28896a54c310 Mon Sep 17 00:00:00 2001 From: Pranav Madapurmath Date: Thu, 25 May 2023 21:58:19 +0000 Subject: [PATCH 171/208] Resolve StatusHints image exploit across user. Because of the INTERACT_ACROSS_USERS permission, an app that implements a ConnectionService can upload an image icon belonging to another user by setting it in the StatusHints. Validating the construction of the StatusHints on the calling user would prevent a malicious app from registering a connection service with the embedded image icon from a different user. From additional feedback, this CL also addresses potential vulnerabilities in an app being able to directly invoke the binder for a means to manipulate the contents of the bundle that are passed with it. The targeted points of entry are in ConnectionServiceWrapper for the following APIs: handleCreateConnectionComplete, setStatusHints, addConferenceCall, and addExistingConnection. Fixes: 280797684 Test: Manual (verified that original exploit is no longer an issue). Test: Unit test for validating image in StatusHints constructor. Test: Unit tests to address vulnerabilities via the binder. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:48223d6034907349c6a3fab3018c1b37d86367af) Merged-In: I6e70e238b3a5ace1cab41ec5796a6bb4d79769f2 Change-Id: I6e70e238b3a5ace1cab41ec5796a6bb4d79769f2 --- .../android/telecom/ParcelableConference.java | 12 ++++- .../java/android/telecom/StatusHints.java | 53 ++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java index 1f8aafbca476..77034041f1fd 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -21,12 +21,12 @@ import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.telecom.IVideoProvider; + import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.android.internal.telecom.IVideoProvider; - /** * A parcelable representation of a conference connection. * @hide @@ -287,6 +287,14 @@ public int getCallDirection() { return mCallDirection; } + public String getCallerDisplayName() { + return mCallerDisplayName; + } + + public int getCallerDisplayNamePresentation() { + return mCallerDisplayNamePresentation; + } + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator () { @Override diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java index 762c93a49022..b7346331dc60 100644 --- a/telecomm/java/android/telecom/StatusHints.java +++ b/telecomm/java/android/telecom/StatusHints.java @@ -16,14 +16,19 @@ package android.telecom; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.os.Binder; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; + +import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; @@ -33,7 +38,7 @@ public final class StatusHints implements Parcelable { private final CharSequence mLabel; - private final Icon mIcon; + private Icon mIcon; private final Bundle mExtras; /** @@ -48,10 +53,30 @@ public StatusHints(ComponentName packageName, CharSequence label, int iconResId, public StatusHints(CharSequence label, Icon icon, Bundle extras) { mLabel = label; - mIcon = icon; + mIcon = validateAccountIconUserBoundary(icon, Binder.getCallingUserHandle()); mExtras = extras; } + /** + * @param icon + * @hide + */ + @VisibleForTesting + public StatusHints(@Nullable Icon icon) { + mLabel = null; + mExtras = null; + mIcon = icon; + } + + /** + * + * @param icon + * @hide + */ + public void setIcon(@Nullable Icon icon) { + mIcon = icon; + } + /** * @return A package used to load the icon. * @@ -112,6 +137,30 @@ public int describeContents() { return 0; } + /** + * Validates the StatusHints image icon to see if it's not in the calling user space. + * Invalidates the icon if so, otherwise returns back the original icon. + * + * @param icon + * @return icon (validated) + * @hide + */ + public static Icon validateAccountIconUserBoundary(Icon icon, UserHandle callingUserHandle) { + // Refer to Icon#getUriString for context. The URI string is invalid for icons of + // incompatible types. + if (icon != null && (icon.getType() == Icon.TYPE_URI + || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { + String encodedUser = icon.getUri().getEncodedUserInfo(); + // If there is no encoded user, the URI is calling into the calling user space + if (encodedUser != null) { + int userId = Integer.parseInt(encodedUser); + // Do not try to save the icon if the user id isn't in the calling user space. + if (userId != callingUserHandle.getIdentifier()) return null; + } + } + return icon; + } + @Override public void writeToParcel(Parcel out, int flags) { out.writeCharSequence(mLabel); From 2bee990f9b9d1107cfcd8f4a7a194264dc00cbaa Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Mon, 3 Jul 2023 16:29:47 +0000 Subject: [PATCH 172/208] DO NOT MERGE Revert "Verify URI permissions for EXTRA_REMOTE_INPUT_HISTORY_ITEMS." This reverts commit 43b1711332763788c7abf05c3baa931296c45bbb. Reason for revert: regression reported at b/289223315 Bug: 289223315 Bug: 276729064 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f9d8830e3264c66d0f39b1d45eadd4039695a112) Merged-In: I101938fbc51592537023345ba1e642827510981b Change-Id: I101938fbc51592537023345ba1e642827510981b --- core/java/android/app/Notification.java | 11 ----------- .../notification/NotificationManagerServiceTest.java | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index abc50300ca68..ce2505591b38 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2533,17 +2533,6 @@ public void visitUris(@NonNull Consumer visitor) { if (person != null && person.getIconUri() != null) { visitor.accept(person.getIconUri()); } - - final RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) - extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); - if (history != null) { - for (int i = 0; i < history.length; i++) { - RemoteInputHistoryItem item = history[i]; - if (item.getUri() != null) { - visitor.accept(item.getUri()); - } - } - } } if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index e5bea2bfaa59..286a02dbd5db 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -102,7 +102,6 @@ import android.app.PendingIntent; import android.app.Person; import android.app.RemoteInput; -import android.app.RemoteInputHistoryItem; import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; @@ -4285,12 +4284,6 @@ public void testVisitUris() throws Exception { .setName("People List Person 2") .setIcon(personIcon3) .build(); - final Uri historyUri1 = Uri.parse("content://com.example/history1"); - final Uri historyUri2 = Uri.parse("content://com.example/history2"); - final RemoteInputHistoryItem historyItem1 = new RemoteInputHistoryItem(null, historyUri1, - "a"); - final RemoteInputHistoryItem historyItem2 = new RemoteInputHistoryItem(null, historyUri2, - "b"); Bundle extras = new Bundle(); extras.putParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI, audioContents); @@ -4298,8 +4291,6 @@ public void testVisitUris() throws Exception { extras.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person1); extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, new ArrayList<>(Arrays.asList(person2, person3))); - extras.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, - new RemoteInputHistoryItem[]{historyItem1, historyItem2}); Notification n = new Notification.Builder(mContext, "a") .setContentTitle("notification with uris") @@ -4317,8 +4308,6 @@ public void testVisitUris() throws Exception { verify(visitor, times(1)).accept(eq(personIcon1.getUri())); verify(visitor, times(1)).accept(eq(personIcon2.getUri())); verify(visitor, times(1)).accept(eq(personIcon3.getUri())); - verify(visitor, times(1)).accept(eq(historyUri1)); - verify(visitor, times(1)).accept(eq(historyUri2)); } @Test From b803f3da13983e6abc4f6a996502eff47a32cde1 Mon Sep 17 00:00:00 2001 From: Aishwarya Mallampati Date: Tue, 1 Nov 2022 17:04:35 +0000 Subject: [PATCH 173/208] DO NOT MERGE Grant carrier privileges if package has carrier config access. TelephonyManager#hasCarrierPrivileges internally uses SubscriptionManager#canManageSubscription to decide whether to grant carrier privilege status to an app or not. SubscriptionManager#canManageSubscription returns true if caller APK's certificate matches with one of the mNativeAccessRules or mCarrierConfigAccessRules. This over-grants carrier privilege status to apps that only has mNativeAccessRules. Carrier privilege status should be granted to the caller APK only if it's certificate matches with one of mCarrierConfigAccessRules. Replaced SubscriptionManager#canManageSubscription with PhoneInterfaceManager#hasCarrierConfigAccess which returns true only if caller APK certificates matches with one of mCarrierConfigAccessRules of the given subscription. Bug: 226593252 Test: Manual Testing as explained in b/226593252#comment51 atest CtsTelephonyTestCases Flashed build on raven-userdebug and performed basic funtionality tests (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e46bce078fef9dba500a7411e843f7f00a7a33c2) Merged-In: I662064529d2a9348f395fe3b541366de8bc2fe7d Change-Id: I662064529d2a9348f395fe3b541366de8bc2fe7d --- telephony/java/android/telephony/SubscriptionInfo.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 90d7a161767c..a8a7f36525a9 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -693,6 +694,15 @@ public boolean canManageSubscription(Context context, String packageName) { return merged.isEmpty() ? null : merged; } + /** + * @hide + * @return mCarrierConfigAccessRules associated with this subscription. + */ + public @NonNull List getCarrierConfigAccessRules() { + return mCarrierConfigAccessRules == null ? Collections.emptyList() : + Arrays.asList(mCarrierConfigAccessRules); + } + /** * Returns the card string of the SIM card which contains the subscription. * From 7b76baccc40cd56800de390968564cf1f2388cc9 Mon Sep 17 00:00:00 2001 From: Achim Thesmann Date: Wed, 14 Jun 2023 05:19:39 +0000 Subject: [PATCH 174/208] Ignore virtual presentation windows - RESTRICT AUTOMERGE Windows of TYPE_PRESENTATION on virtual displays should not be counted as visible windows to determine if BAL is allowed. Test: manual test, atest BackgroundActivityLaunchTest Bug: 264029851, 205130886 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:07019d9060d31d12b1d2cbb10635dfaa1b36c95c) Merged-In: I0f6bc691ca50582cb56e3b8a7ade14eac1660358 Change-Id: I0f6bc691ca50582cb56e3b8a7ade14eac1660358 --- services/core/java/com/android/server/wm/WindowState.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7e0acab1faa1..7360e3677def 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -61,7 +61,6 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; -import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; @@ -5828,7 +5827,8 @@ void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, */ boolean isNonToastOrStartingOrPrivatePresentation() { return mAttrs.type != TYPE_TOAST && mAttrs.type != TYPE_APPLICATION_STARTING - && mAttrs.type != TYPE_PRIVATE_PRESENTATION; + && mAttrs.type != TYPE_PRIVATE_PRESENTATION + && !(mAttrs.type == TYPE_PRESENTATION && isOnVirtualDisplay()); } boolean isNonToastWindowVisibleForUid(int callingUid) { @@ -5841,6 +5841,10 @@ boolean isNonToastWindowVisibleForPid(int pid) { && isVisibleNow(); } + private boolean isOnVirtualDisplay() { + return getDisplayContent().mDisplay.getType() == Display.TYPE_VIRTUAL; + } + void setViewVisibility(int viewVisibility) { mViewVisibility = viewVisibility; // The viewVisibility is set to GONE with a client request to relayout. If this occurs and From 3712b403a1fa3844f8a3686257ecf8edfbd4b230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Thu, 15 Jun 2023 18:31:34 +0200 Subject: [PATCH 175/208] Forbid granting access to NLSes with too-long component names This makes the limitation, which was previously only checked on the Settings UI, enforced everywhere. Fixes: 260570119 Fixes: 286043036 Test: atest + manually (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:dc71156a29427c8b228129f5b1368392f297835b) Merged-In: I4c25d80978cb37a8fa1531f5045259d25ac64692 Change-Id: I4c25d80978cb37a8fa1531f5045259d25ac64692 --- .../java/android/app/NotificationManager.java | 6 ++++ .../NotificationManagerService.java | 5 ++++ .../android/server/vr/VrManagerService.java | 6 +++- .../NotificationManagerServiceTest.java | 28 +++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 553cc91fdd5f..f9e19b5fa7de 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -465,6 +465,12 @@ public class NotificationManager { */ public static final int BUBBLE_PREFERENCE_SELECTED = 2; + /** + * Maximum length of the component name of a registered NotificationListenerService. + * @hide + */ + public static int MAX_SERVICE_COMPONENT_NAME_LENGTH = 500; + @UnsupportedAppUsage private static INotificationManager sService; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 14454596db3d..caf7d70edb09 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4982,6 +4982,11 @@ public void setNotificationListenerAccessGrantedForUser(ComponentName listener, boolean granted) { Objects.requireNonNull(listener); checkCallerIsSystemOrShell(); + if (granted && listener.flattenToString().length() + > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { + throw new IllegalArgumentException( + "Component name too long: " + listener.flattenToString()); + } final long identity = Binder.clearCallingIdentity(); try { if (mAllowedManagedServicePackages.test( diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 45689ce73c9f..7eeba02542e4 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -1045,7 +1045,11 @@ private void grantNotificationListenerAccess(String pkg, int userId) { for (ComponentName c : possibleServices) { if (Objects.equals(c.getPackageName(), pkg)) { - nm.setNotificationListenerAccessGrantedForUser(c, userId, true); + try { + nm.setNotificationListenerAccessGrantedForUser(c, userId, true); + } catch (Exception e) { + Slog.w(TAG, "Could not grant NLS access to package " + pkg, e); + } } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 286a02dbd5db..feaebe92a34e 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3165,6 +3165,34 @@ public void testSetListenerAccessForUser() throws Exception { any(), anyInt(), anyBoolean(), anyBoolean()); } + @Test + public void testSetListenerAccessForUser_grantWithNameTooLong_throws() throws Exception { + UserHandle user = UserHandle.of(mContext.getUserId() + 10); + ComponentName c = new ComponentName("com.example.package", + com.google.common.base.Strings.repeat("Blah", 150)); + + try { + mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), + /* enabled= */ true); + fail("Should've thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Good! + } + } + + @Test + public void testSetListenerAccessForUser_revokeWithNameTooLong_okay() throws Exception { + UserHandle user = UserHandle.of(mContext.getUserId() + 10); + ComponentName c = new ComponentName("com.example.package", + com.google.common.base.Strings.repeat("Blah", 150)); + + mBinderService.setNotificationListenerAccessGrantedForUser( + c, user.getIdentifier(), /* enabled= */ false); + + verify(mListeners).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, /* enabled= */ false); + } + @Test public void testSetAssistantAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); From c079856eca238c79b8484f44a55fd39a6c0734b3 Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Fri, 30 Jun 2023 14:36:44 -0700 Subject: [PATCH 176/208] Update AccountManagerService checkKeyIntentParceledCorrectly. Bug: 265798288 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b117b506ec0504ff9eb2fa523e82f1879ecb8cc1) Merged-In: Iad33851af32a11c99d11bc2b5c76d124c3e97ebb Change-Id: Iad33851af32a11c99d11bc2b5c76d124c3e97ebb --- .../com/android/server/accounts/AccountManagerService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 545373c289bc..ae72a65692e7 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4840,6 +4840,9 @@ private boolean checkKeyIntentParceledCorrectly(Bundle bundle) { Bundle simulateBundle = p.readBundle(); p.recycle(); Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + if (intent != null && intent.getClass() != Intent.class) { + return false; + } Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT); if (intent == null) { return (simulateIntent == null); From fdd8adc7ba76083c7ec151cc084dc88ef1a879f1 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Thu, 22 Jun 2023 18:26:44 -0500 Subject: [PATCH 177/208] Improve user handling when querying for resumable media - Before trying to query recent media from a saved component, check whether the current user actually has that component installed - Track user when creating the MediaBrowser, in case the user changes before the MBS returns a result Test: atest MediaResumeListenerTest Bug: 284297711 (cherry picked from commit e566a250ad61e269119b475c7ebdae6ca962c4a7) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:684492a0dfb0c045a07906a2a79d5e785e3b794d) Merged-In: I838ff0e125acadabc8436a00dbff707cc4be6249 Change-Id: I838ff0e125acadabc8436a00dbff707cc4be6249 --- .../systemui/media/MediaResumeListener.kt | 42 +++++++--- .../systemui/media/ResumeMediaBrowser.java | 17 +++- .../media/ResumeMediaBrowserFactory.java | 7 +- .../systemui/media/MediaResumeListenerTest.kt | 80 ++++++++++++++++++- 4 files changed, 132 insertions(+), 14 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 936db8735ad8..b1699d7194bc 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -90,9 +90,16 @@ class MediaResumeListener @Inject constructor( Log.e(TAG, "Error getting package information", e) } - Log.d(TAG, "Adding resume controls $desc") - mediaDataManager.addResumptionControls(currentUserId, desc, resumeAction, token, - appName.toString(), appIntent, component.packageName) + Log.d(TAG, "Adding resume controls for ${browser.userId}: $desc") + mediaDataManager.addResumptionControls( + browser.userId, + desc, + resumeAction, + token, + appName.toString(), + appIntent, + component.packageName + ) } } @@ -133,7 +140,11 @@ class MediaResumeListener @Inject constructor( val component = ComponentName(packageName, className) resumeComponents.add(component) } - Log.d(TAG, "loaded resume components ${resumeComponents.toArray().contentToString()}") + Log.d( + TAG, + "loaded resume components for $currentUserId: " + + "${resumeComponents.toArray().contentToString()}" + ) } /** @@ -144,9 +155,19 @@ class MediaResumeListener @Inject constructor( return } + val pm = context.packageManager resumeComponents.forEach { - val browser = mediaBrowserFactory.create(mediaBrowserCallback, it) - browser.findRecentMedia() + // Verify that the service exists for this user + val intent = Intent(MediaBrowserService.SERVICE_INTERFACE) + intent.component = it + val inf = pm.resolveServiceAsUser(intent, 0, currentUserId) + if (inf != null) { + val browser = + mediaBrowserFactory.create(mediaBrowserCallback, it, currentUserId) + browser.findRecentMedia() + } else { + Log.d(TAG, "User $currentUserId does not have component $it") + } } } @@ -160,7 +181,7 @@ class MediaResumeListener @Inject constructor( Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE) - val resumeInfo = pm.queryIntentServices(serviceIntent, 0) + val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId) val inf = resumeInfo?.filter { it.serviceInfo.packageName == data.packageName @@ -210,7 +231,9 @@ class MediaResumeListener @Inject constructor( mediaBrowser = null } }, - componentName) + componentName, + currentUserId + ) mediaBrowser?.testConnection() } @@ -267,7 +290,8 @@ class MediaResumeListener @Inject constructor( mediaBrowser = null } }, - componentName) + componentName, + currentUserId) mediaBrowser?.restart() } } diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java index a4d44367be73..a17dc5d7d2c2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java +++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java @@ -16,6 +16,7 @@ package com.android.systemui.media; +import android.annotation.UserIdInt; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -49,6 +50,8 @@ public class ResumeMediaBrowser { private final Context mContext; private final Callback mCallback; private MediaBrowserFactory mBrowserFactory; + @UserIdInt private final int mUserId; + private MediaBrowser mMediaBrowser; private ComponentName mComponentName; @@ -57,13 +60,15 @@ public class ResumeMediaBrowser { * @param context the context * @param callback used to report media items found * @param componentName Component name of the MediaBrowserService this browser will connect to + * @param userId ID of the current user */ public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName, - MediaBrowserFactory browserFactory) { + MediaBrowserFactory browserFactory, @UserIdInt int userId) { mContext = context; mCallback = callback; mComponentName = componentName; mBrowserFactory = browserFactory; + mUserId = userId; } /** @@ -224,6 +229,14 @@ protected MediaController createMediaController(MediaSession.Token token) { return new MediaController(mContext, token); } + /** + * Get the ID of the user associated with this broswer + * @return the user ID + */ + public @UserIdInt int getUserId() { + return mUserId; + } + /** * Get the media session token * @return the token, or null if the MediaBrowser is null or disconnected @@ -257,7 +270,7 @@ public void testConnection() { disconnect(); Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); - mMediaBrowser = mBrowserFactory.create( + mMediaBrowser = mBrowserFactory.create( mComponentName, mConnectionCallback, rootHints); diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java index 2261aa5ac265..5744190cfe63 100644 --- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java +++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java @@ -16,6 +16,7 @@ package com.android.systemui.media; +import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Context; @@ -39,10 +40,12 @@ public ResumeMediaBrowserFactory(Context context, MediaBrowserFactory browserFac * * @param callback will be called on connection or error, and addTrack when media item found * @param componentName component to browse + * @param userId ID of the current user * @return */ public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback, - ComponentName componentName) { - return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory); + ComponentName componentName, @UserIdInt int userId) { + return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory, + userId); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt index 5d81de6bce00..e4ca06e832a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -87,6 +87,7 @@ class MediaResumeListenerTest : SysuiTestCase() { @Mock private lateinit var pendingIntent: PendingIntent @Captor lateinit var callbackCaptor: ArgumentCaptor + @Captor lateinit var userIdCaptor: ArgumentCaptor private lateinit var executor: FakeExecutor private lateinit var data: MediaData @@ -106,7 +107,7 @@ class MediaResumeListenerTest : SysuiTestCase() { Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1) - whenever(resumeBrowserFactory.create(capture(callbackCaptor), any())) + whenever(resumeBrowserFactory.create(capture(callbackCaptor), any(), capture(userIdCaptor))) .thenReturn(resumeBrowser) // resume components are stored in sharedpreferences @@ -117,6 +118,7 @@ class MediaResumeListenerTest : SysuiTestCase() { whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor) whenever(mockContext.packageManager).thenReturn(context.packageManager) whenever(mockContext.contentResolver).thenReturn(context.contentResolver) + whenever(mockContext.userId).thenReturn(context.userId) executor = FakeExecutor(FakeSystemClock()) resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, @@ -255,4 +257,80 @@ class MediaResumeListenerTest : SysuiTestCase() { verify(mediaDataManager, times(3)).addResumptionControls(anyInt(), any(), any(), any(), any(), any(), eq(PACKAGE_NAME)) } + + @Test + fun testUserUnlocked_userChangeWhileQuerying() { + val firstUserId = context.userId + val secondUserId = firstUserId + 1 + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + + setUpMbsWithValidResolveInfo() + whenever(resumeBrowser.token).thenReturn(token) + whenever(resumeBrowser.appIntent).thenReturn(pendingIntent) + + val unlockIntent = + Intent(Intent.ACTION_USER_UNLOCKED).apply { + putExtra(Intent.EXTRA_USER_HANDLE, firstUserId) + } + // When the first user unlocks and we query their recent media + resumeListener.userChangeReceiver.onReceive(context, unlockIntent) + whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value) + verify(resumeBrowser, times(3)).findRecentMedia() + + // And the user changes before the MBS response is received + val changeIntent = + Intent(Intent.ACTION_USER_SWITCHED).apply { + putExtra(Intent.EXTRA_USER_HANDLE, secondUserId) + } + resumeListener.userChangeReceiver.onReceive(context, changeIntent) + callbackCaptor.value.addTrack(description, component, resumeBrowser) + + // Then the loaded media is correctly associated with the first user + verify(mediaDataManager) + .addResumptionControls( + eq(firstUserId), + eq(description), + any(), + eq(token), + eq(PACKAGE_NAME), + eq(pendingIntent), + eq(PACKAGE_NAME) + ) + } + + @Test + fun testUserUnlocked_noComponent_doesNotQuery() { + // Set up a valid MBS, but user does not have the service available + setUpMbsWithValidResolveInfo() + val pm = mock(PackageManager::class.java) + whenever(mockContext.packageManager).thenReturn(pm) + whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(null) + + val unlockIntent = + Intent(Intent.ACTION_USER_UNLOCKED).apply { + putExtra(Intent.EXTRA_USER_HANDLE, context.userId) + } + + // When the user is unlocked, but does not have the component installed + resumeListener.userChangeReceiver.onReceive(context, unlockIntent) + + // Then we never attempt to connect to it + verify(resumeBrowser, never()).findRecentMedia() + } + + /** Sets up mocks to successfully find a MBS that returns valid media. */ + private fun setUpMbsWithValidResolveInfo() { + val pm = mock(PackageManager::class.java) + whenever(mockContext.packageManager).thenReturn(pm) + val resolveInfo = ResolveInfo() + val serviceInfo = ServiceInfo() + serviceInfo.packageName = PACKAGE_NAME + resolveInfo.serviceInfo = serviceInfo + resolveInfo.serviceInfo.name = CLASS_NAME + val resumeInfo = listOf(resolveInfo) + whenever(pm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(resumeInfo) + whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo) + whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME) + } } \ No newline at end of file From a17d2aa1b359e509d7771f426bf016625bd3196e Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 7 Dec 2022 04:36:46 +0000 Subject: [PATCH 178/208] RingtoneManager: verify default ringtone is audio When a ringtone picker tries to set a ringtone through RingtoneManager.setActualDefaultRingtoneUri (also called by com.android.settings.DefaultRingtonePreference), verify the mimeType can be obtained (not found when caller doesn't have access to it) and it is an audio resource. Bug: 205837340 Test: atest android.media.audio.cts.RingtoneManagerTest (cherry picked from commit 38618f9fb16d3b5617e2289354d47abe5af17dad) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:377144b64325dadad102f5233ecb50a4446b205b) Merged-In: I3f2c487ded405c0c1a83ef0a2fe99cff7cc9328e Change-Id: I3f2c487ded405c0c1a83ef0a2fe99cff7cc9328e --- media/java/android/media/RingtoneManager.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 9deeb8fbab16..918a9d8943dd 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -801,10 +801,10 @@ public static Uri getActualDefaultRingtoneUri(Context context, int type) { return ringtoneUri; } - + /** * Sets the {@link Uri} of the default sound for a given sound type. - * + * * @param context A context used for querying. * @param type The type whose default sound should be set. One of * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or @@ -825,6 +825,21 @@ public static void setActualDefaultRingtoneUri(Context context, int type, Uri ri if(!isInternalRingtoneUri(ringtoneUri)) { ringtoneUri = ContentProvider.maybeAddUserId(ringtoneUri, context.getUserId()); } + + if (ringtoneUri != null) { + final String mimeType = resolver.getType(ringtoneUri); + if (mimeType == null) { + Log.e(TAG, "setActualDefaultRingtoneUri for URI:" + ringtoneUri + + " ignored: failure to find mimeType (no access from this context?)"); + return; + } + if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg"))) { + Log.e(TAG, "setActualDefaultRingtoneUri for URI:" + ringtoneUri + + " ignored: associated mimeType:" + mimeType + " is not an audio type"); + return; + } + } + Settings.System.putStringForUser(resolver, setting, ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId()); From 5fc03f01916a685ec1e3e0615bfe3b6b5132e453 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Tue, 30 May 2023 18:45:47 -0500 Subject: [PATCH 179/208] Add placeholder when media control title is blank When an app posts a media control with no available title, show a placeholder string with the app name instead Bug: 274775190 Test: atest MediaDataManagerTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:070eff919c85fd83501e380a92e30caf082e9ffc) Merged-In: Ie406c180af48653595e8e222a15b4dda27de2e0e Change-Id: Ie406c180af48653595e8e222a15b4dda27de2e0e --- packages/SystemUI/res/values/strings.xml | 2 + .../systemui/media/MediaDataManager.kt | 12 ++- .../systemui/media/MediaDataManagerTest.kt | 94 ++++++++++++++++++- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 37952c437e8d..1e7ffffc26e3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2834,6 +2834,8 @@ Resume Settings + + %1$s is running Inactive, check app diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 2d7aafc497d8..a758449f8c88 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -445,13 +445,17 @@ class MediaDataManager( val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context) // Song name - var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) - if (song == null) { - song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE) + var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) + if (song.isNullOrBlank()) { + song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE) } - if (song == null) { + if (song.isNullOrBlank()) { song = HybridGroupManager.resolveTitle(notif) } + if (song.isNullOrBlank()) { + // For apps that don't include a title, add a placeholder + song = context.getString(R.string.controls_media_empty_title, app) + } // Artist name var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 2f99b2a2a449..780e64af3b4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -11,6 +11,7 @@ import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager @@ -39,9 +40,11 @@ import org.mockito.Mockito.`when` as whenever private const val KEY = "KEY" private const val KEY_2 = "KEY_2" private const val PACKAGE_NAME = "com.android.systemui" -private const val APP_NAME = "SystemUI" +private const val APP_NAME = "com.android.systemui.tests" private const val SESSION_ARTIST = "artist" private const val SESSION_TITLE = "title" +private const val SESSION_BLANK_TITLE = " " +private const val SESSION_EMPTY_TITLE = "" private const val USER_ID = 0 private fun anyObject(): T { @@ -168,6 +171,95 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } + @Test + fun testOnNotificationAdded_emptyTitle_hasPlaceholder() { + // When the manager has a notification with an empty title + val listener = mock(MediaDataManager.Listener::class.java) + mediaDataManager.addListener(listener) + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE) + .build() + ) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + + // Then a media control is created with a placeholder title string + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(null), + capture(mediaDataCaptor) + ) + val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME) + assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle) + } + + @Test + fun testOnNotificationAdded_blankTitle_hasPlaceholder() { + // GIVEN that the manager has a notification with a blank title + val listener = mock(MediaDataManager.Listener::class.java) + mediaDataManager.addListener(listener) + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE) + .build() + ) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + + // Then a media control is created with a placeholder title string + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(null), + capture(mediaDataCaptor) + ) + val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME) + assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle) + } + + @Test + fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() { + // When the app sets the metadata title fields to empty strings, but does include a + // non-blank notification title + val listener = mock(MediaDataManager.Listener::class.java) + mediaDataManager.addListener(listener) + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE) + .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE) + .build() + ) + mediaNotification = + SbnBuilder().run { + setPkg(PACKAGE_NAME) + modifyNotification(context).also { + it.setSmallIcon(android.R.drawable.ic_media_pause) + it.setContentTitle(SESSION_TITLE) + it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) }) + } + build() + } + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + + // Then the media control is added using the notification's title + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(null), + capture(mediaDataCaptor) + ) + assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE) + } + @Test fun testOnNotificationRemoved_withResumption() { // GIVEN that the manager has a notification with a resume action From 16a8860cb0b53abf3ca3c47d63719ae2cc851be5 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 21 Jun 2023 13:20:27 -0700 Subject: [PATCH 180/208] Import translations. DO NOT MERGE ANYWHERE BUG:286996125 Auto-generated-cl: translation import (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:917729f52b84d8ff1140ece2dd696914426adcd3) Merged-In: I9f1045f17a003528a57cb8e9dfc53e5df40579f3 Change-Id: I9f1045f17a003528a57cb8e9dfc53e5df40579f3 --- packages/SystemUI/res/values-af/strings.xml | 1 + packages/SystemUI/res/values-am/strings.xml | 1 + packages/SystemUI/res/values-ar/strings.xml | 1 + packages/SystemUI/res/values-as/strings.xml | 1 + packages/SystemUI/res/values-az/strings.xml | 1 + packages/SystemUI/res/values-b+sr+Latn/strings.xml | 1 + packages/SystemUI/res/values-be/strings.xml | 1 + packages/SystemUI/res/values-bg/strings.xml | 1 + packages/SystemUI/res/values-bn/strings.xml | 1 + packages/SystemUI/res/values-bs/strings.xml | 1 + packages/SystemUI/res/values-ca/strings.xml | 1 + packages/SystemUI/res/values-cs/strings.xml | 1 + packages/SystemUI/res/values-da/strings.xml | 1 + packages/SystemUI/res/values-de/strings.xml | 1 + packages/SystemUI/res/values-el/strings.xml | 1 + packages/SystemUI/res/values-en-rAU/strings.xml | 1 + packages/SystemUI/res/values-en-rCA/strings.xml | 1 + packages/SystemUI/res/values-en-rGB/strings.xml | 1 + packages/SystemUI/res/values-en-rIN/strings.xml | 1 + packages/SystemUI/res/values-en-rXC/strings.xml | 1 + packages/SystemUI/res/values-es-rUS/strings.xml | 1 + packages/SystemUI/res/values-es/strings.xml | 1 + packages/SystemUI/res/values-et/strings.xml | 1 + packages/SystemUI/res/values-eu/strings.xml | 1 + packages/SystemUI/res/values-fa/strings.xml | 1 + packages/SystemUI/res/values-fi/strings.xml | 1 + packages/SystemUI/res/values-fr-rCA/strings.xml | 1 + packages/SystemUI/res/values-fr/strings.xml | 1 + packages/SystemUI/res/values-gl/strings.xml | 1 + packages/SystemUI/res/values-gu/strings.xml | 1 + packages/SystemUI/res/values-hi/strings.xml | 1 + packages/SystemUI/res/values-hr/strings.xml | 1 + packages/SystemUI/res/values-hu/strings.xml | 1 + packages/SystemUI/res/values-hy/strings.xml | 1 + packages/SystemUI/res/values-in/strings.xml | 1 + packages/SystemUI/res/values-is/strings.xml | 1 + packages/SystemUI/res/values-it/strings.xml | 1 + packages/SystemUI/res/values-iw/strings.xml | 1 + packages/SystemUI/res/values-ja/strings.xml | 1 + packages/SystemUI/res/values-ka/strings.xml | 1 + packages/SystemUI/res/values-kk/strings.xml | 1 + packages/SystemUI/res/values-km/strings.xml | 1 + packages/SystemUI/res/values-kn/strings.xml | 1 + packages/SystemUI/res/values-ko/strings.xml | 1 + packages/SystemUI/res/values-ky/strings.xml | 1 + packages/SystemUI/res/values-lo/strings.xml | 1 + packages/SystemUI/res/values-lt/strings.xml | 1 + packages/SystemUI/res/values-lv/strings.xml | 1 + packages/SystemUI/res/values-mk/strings.xml | 1 + packages/SystemUI/res/values-ml/strings.xml | 1 + packages/SystemUI/res/values-mn/strings.xml | 1 + packages/SystemUI/res/values-mr/strings.xml | 1 + packages/SystemUI/res/values-ms/strings.xml | 1 + packages/SystemUI/res/values-my/strings.xml | 1 + packages/SystemUI/res/values-nb/strings.xml | 1 + packages/SystemUI/res/values-ne/strings.xml | 1 + packages/SystemUI/res/values-nl/strings.xml | 1 + packages/SystemUI/res/values-or/strings.xml | 1 + packages/SystemUI/res/values-pa/strings.xml | 1 + packages/SystemUI/res/values-pl/strings.xml | 1 + packages/SystemUI/res/values-pt-rBR/strings.xml | 1 + packages/SystemUI/res/values-pt-rPT/strings.xml | 1 + packages/SystemUI/res/values-pt/strings.xml | 1 + packages/SystemUI/res/values-ro/strings.xml | 1 + packages/SystemUI/res/values-ru/strings.xml | 1 + packages/SystemUI/res/values-si/strings.xml | 1 + packages/SystemUI/res/values-sk/strings.xml | 1 + packages/SystemUI/res/values-sl/strings.xml | 1 + packages/SystemUI/res/values-sq/strings.xml | 1 + packages/SystemUI/res/values-sr/strings.xml | 1 + packages/SystemUI/res/values-sv/strings.xml | 1 + packages/SystemUI/res/values-sw/strings.xml | 1 + packages/SystemUI/res/values-ta/strings.xml | 1 + packages/SystemUI/res/values-te/strings.xml | 1 + packages/SystemUI/res/values-th/strings.xml | 1 + packages/SystemUI/res/values-tl/strings.xml | 1 + packages/SystemUI/res/values-tr/strings.xml | 1 + packages/SystemUI/res/values-uk/strings.xml | 1 + packages/SystemUI/res/values-ur/strings.xml | 1 + packages/SystemUI/res/values-uz/strings.xml | 1 + packages/SystemUI/res/values-vi/strings.xml | 1 + packages/SystemUI/res/values-zh-rCN/strings.xml | 1 + packages/SystemUI/res/values-zh-rHK/strings.xml | 1 + packages/SystemUI/res/values-zh-rTW/strings.xml | 1 + packages/SystemUI/res/values-zu/strings.xml | 1 + 85 files changed, 85 insertions(+) diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index ad70a67421b9..628608e36160 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -1079,6 +1079,7 @@ "Maak toe" "Hervat" "Instellings" + "%1$s loop tans" "Onaktief, gaan program na" "Fout, probeer tans weer …" "Nie gekry nie" diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 2900060b84b6..0af1f4619b8b 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -1079,6 +1079,7 @@ "አሰናብት" "ከቆመበት ቀጥል" "ቅንብሮች" + "%1$s እያሄደ ነው" "ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ" "ስህተት፣ እንደገና በመሞከር ላይ…" "አልተገኘም" diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 3d38d5ed088c..4f1f5e6f27f1 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -1103,6 +1103,7 @@ "إغلاق" "استئناف التشغيل" "الإعدادات" + "\"%1$s\" قيد التشغيل" "غير نشط، تحقّق من التطبيق." "حدث خطأ، جارٍ إعادة المحاولة…" "لم يتم العثور عليه." diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index c8e5e3675489..7cb23613ec7f 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -1079,6 +1079,7 @@ "অগ্ৰাহ্য কৰক" "পুনৰ আৰম্ভ কৰক" "ছেটিংসমূহ" + "%1$s চলি আছে" "সক্ৰিয় নহয়, এপ্‌টো পৰীক্ষা কৰক" "আসোঁৱাহ, পুনৰ চেষ্টা কৰি আছে…" "বিচাৰি পোৱা নগ’ল" diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 701927f32714..cfdb8ef85541 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1079,6 +1079,7 @@ "İmtina edin" "Davam edin" "Ayarlar" + "%1$s işləyir" "Aktiv deyil, tətbiqi yoxlayın" "Xəta, yenidən cəhd edilir…" "Tapılmadı" diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 6c01ceed9fff..1a1817783190 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -1085,6 +1085,7 @@ "Odbaci" "Nastavi" "Podešavanja" + "Aplikacija %1$s je pokrenuta" "Neaktivno. Vidite aplikaciju" "Greška, pokušava se ponovo…" "Nije pronađeno" diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index bcc534790ed4..85e3dc9171f6 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -1091,6 +1091,7 @@ "Адхіліць" "Узнавіць" "Налады" + "%1$s працуе" "Неактыўна, праверце праграму" "Памылка, паўторная спроба…" "Не знойдзена" diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6b8844d67d01..a60b2681e549 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -1079,6 +1079,7 @@ "Отхвърляне" "Възобновяване" "Настройки" + "%1$s се изпълнява" "Неактивно, проверете прилож." "Грешка. Извършва се нов опит…" "Не е намерено" diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index d68f46337f2b..7848ec133f58 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -1079,6 +1079,7 @@ "খারিজ করুন" "আবার চালু করুন" "সেটিংস" + "%1$s চলছে" "বন্ধ আছে, অ্যাপ চেক করুন" "সমস্যা, আবার চেষ্টা করা হচ্ছে…" "খুঁজে পাওয়া যায়নি" diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index d3ef3ba9eff6..3ab91c389a58 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -1085,6 +1085,7 @@ "Odbaci" "Nastavi" "Postavke" + "Aplikacija %1$s je pokrenuta" "Neaktivno, vidite aplikaciju" "Greška, ponovni pokušaj…" "Nije pronađeno" diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 251464d3cee8..7bc747a236fa 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -1079,6 +1079,7 @@ "Ignora" "Reprèn" "Configuració" + "%1$s s\'està executant" "Inactiu; comprova l\'aplicació" "Error. S\'està tornant a provar…" "No s\'ha trobat" diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index e4b32f9fe1a0..f61337f32aca 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -1091,6 +1091,7 @@ "Zavřít" "Pokračovat" "Nastavení" + "Aplikace %1$s je spuštěna" "Neaktivní, zkontrolujte aplikaci" "Chyba. Nový pokus…" "Nenalezeno" diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 66885745b479..59bc8408fdc1 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -1079,6 +1079,7 @@ "Luk" "Genoptag" "Indstillinger" + "%1$s kører" "Inaktiv. Tjek appen" "Fejl. Prøver igen…" "Ikke fundet" diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 3b37ca3c4add..6b242519cb12 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -1079,6 +1079,7 @@ "Ablehnen" "Fortsetzen" "Einstellungen" + "%1$s wird ausgeführt" "Inaktiv – sieh in der App nach" "Fehler. Neuer Versuch…" "Nicht gefunden" diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 89331973c532..fb1dda51af3d 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -1079,6 +1079,7 @@ "Παράβλεψη" "Συνέχιση" "Ρυθμίσεις" + "Η εφαρμογή %1$s εκτελείται" "Ανενεργό, έλεγχος εφαρμογής" "Προέκυψε σφάλμα. Επανάληψη…" "Δεν βρέθηκε." diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 0adf19447e8c..f5bbb71946a3 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -1079,6 +1079,7 @@ "Dismiss" "Resume" "Settings" + "%1$s is running" "Inactive, check app" "Error, retrying…" "Not found" diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 9265f7141050..0c083c785002 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -1079,6 +1079,7 @@ "Dismiss" "Resume" "Settings" + "%1$s is running" "Inactive, check app" "Error, retrying…" "Not found" diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 0adf19447e8c..f5bbb71946a3 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -1079,6 +1079,7 @@ "Dismiss" "Resume" "Settings" + "%1$s is running" "Inactive, check app" "Error, retrying…" "Not found" diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 0adf19447e8c..f5bbb71946a3 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -1079,6 +1079,7 @@ "Dismiss" "Resume" "Settings" + "%1$s is running" "Inactive, check app" "Error, retrying…" "Not found" diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 2120e835c308..a03048fb7dbe 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -1079,6 +1079,7 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎Dismiss‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎Resume‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎Settings‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ is running‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎Error, retrying…‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎Not found‎‏‎‎‏‎" diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 0a3df72bba05..1643a33b4962 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -1079,6 +1079,7 @@ "Descartar" "Reanudar" "Configuración" + "%1$s se está ejecutando" "Inactivo. Verifica la app" "Hubo un error. Reintentando…" "No se encontró" diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 8eb5bf50758b..e9807db232ae 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -1079,6 +1079,7 @@ "Cerrar" "Reanudar" "Ajustes" + "%1$s se está ejecutando" "Inactivo, comprobar aplicación" "Error; reintentando…" "No se ha encontrado" diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index a41e69e9f91e..d7e385ab75ce 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -1079,6 +1079,7 @@ "Loobu" "Jätka" "Seaded" + "%1$s töötab" "Passiivne, vaadake rakendust" "Viga, proovitakse uuesti …" "Ei leitud" diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 8922c85fdbca..1e01faa89336 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -1079,6 +1079,7 @@ "Baztertu" "Berrekin" "Ezarpenak" + "%1$s abian da" "Inaktibo; egiaztatu aplikazioa" "Errorea. Berriro saiatzen…" "Ez da aurkitu" diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index c2872a8da59e..f8799168dff4 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -1079,6 +1079,7 @@ "رد کردن" "ازسرگیری" "تنظیمات" + "%1$s در حال اجرا است" "غیرفعال، برنامه را بررسی کنید" "خطا، درحال تلاش مجدد…" "پیدا نشد" diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 19794e0a4199..449f1f3dc141 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -1079,6 +1079,7 @@ "Ohita" "Jatka" "Asetukset" + "%1$s on käynnissä" "Epäaktiivinen, tarkista sovellus" "Virhe, yritetään uudelleen…" "Ei löydy" diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 0459ca945702..7098f780fede 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -1079,6 +1079,7 @@ "Fermer" "Reprendre" "Paramètres" + "%1$s en cours d\'exécution" "Délai expiré, vérifiez l\'appli" "Erreur, nouvelle tentative…" "Introuvable" diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 7b6ad6bd7b52..8499b2cf366f 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -1079,6 +1079,7 @@ "Fermer" "Reprendre" "Paramètres" + "%1$s est en cours d\'exécution" "Délai expiré, vérifier l\'appli" "Erreur. Nouvelle tentative…" "Introuvable" diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index a210c33297d9..3111bf4e192a 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -1079,6 +1079,7 @@ "Ignorar" "Retomar" "Configuración" + "%1$s estase executando" "Inactivo. Comproba a app" "Erro. Tentando de novo…" "Non se atopou" diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index db782167c7a4..46eee2db2f87 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -1079,6 +1079,7 @@ "છોડી દો" "ફરી શરૂ કરો" "સેટિંગ" + "%1$s ચાલી રહી છે" "નિષ્ક્રિય, ઍપને ચેક કરો" "ભૂલ, ફરી પ્રયાસ કરી રહ્યા છીએ…" "મળ્યું નથી" diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 766a45cff538..07576bdcd3f2 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -1079,6 +1079,7 @@ "खारिज करें" "फिर से शुरू करें" "सेटिंग" + "%1$s चालू है" "काम नहीं कर रहा, ऐप जांचें" "कोई गड़बड़ी हुई, फिर से कोशिश की जा रही है…" "कंट्रोल नहीं है" diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index d22c319cc0e5..57613aadee2f 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -1085,6 +1085,7 @@ "Odbaci" "Nastavi" "Postavke" + "Aplikacija %1$s je pokrenuta" "Neaktivno, provjerite aplik." "Pogreška, pokušavamo ponovo…" "Nije pronađeno" diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 0a37bf8f75c5..fa2cf6adf25c 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -1079,6 +1079,7 @@ "Elvetés" "Folytatás" "Beállítások" + "A(z) %1$s jelenleg fut" "Inaktív, ellenőrizze az appot" "Hiba, újrapróbálkozás…" "Nem található" diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index cfd0c0bec4d1..f2887e2ba8ce 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -1079,6 +1079,7 @@ "Փակել" "Շարունակել" "Կարգավորումներ" + "%1$s հավելվածն աշխատում է" "Ակտիվ չէ, ստուգեք հավելվածը" "Սխալ. նորից ենք փորձում…" "Չի գտնվել" diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 85e58f387c7e..1309a123c5f9 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -1079,6 +1079,7 @@ "Tutup" "Lanjutkan" "Setelan" + "%1$s sedang berjalan" "Nonaktif, periksa aplikasi" "Error, mencoba lagi..." "Tidak ditemukan" diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 324abb508490..36bc1421b8fa 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -1079,6 +1079,7 @@ "Hunsa" "Halda áfram" "Stillingar" + "%1$s er í gangi" "Óvirkt, athugaðu forrit" "Villa, reynir aftur…" "Fannst ekki" diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 8dcb5bc20303..85bbc152eb38 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1079,6 +1079,7 @@ "Ignora" "Riprendi" "Impostazioni" + "%1$s è in esecuzione" "Inattivo, controlla l\'app" "Errore. Nuovo tentativo…" "Controllo non trovato" diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 33a7621853db..8bb79b9b8a6e 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -1091,6 +1091,7 @@ "סגירה" "המשך" "הגדרות" + "אפליקציית %1$s פועלת" "לא פעיל, יש לבדוק את האפליקציה" "שגיאה, מתבצע ניסיון חוזר…" "לא נמצא" diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 91488fa7094f..da2454e34ce8 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -1079,6 +1079,7 @@ "閉じる" "再開" "設定" + "%1$s を実行しています" "無効: アプリをご確認ください" "エラー。再試行しています…" "見つかりませんでした" diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index a2e1b7f25198..bf4ffc3b0cda 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -1079,6 +1079,7 @@ "დახურვა" "გაგრძელება" "პარამეტრები" + "%1$s გაშვებულია" "არააქტიურია, გადაამოწმეთ აპი" "შეცდომა, ხელახალი მცდელობა…" "ვერ მოიძებნა" diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 331f25e1a616..e4556d4a16e6 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -1079,6 +1079,7 @@ "Жабу" "Жалғастыру" "Параметрлер" + "%1$s қосулы тұр" "Өшірулі. Қолданба тексеріңіз." "Қате, әрекет қайталануда…" "Табылмады" diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 1020948bf631..c39c9be9f0d4 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -1079,6 +1079,7 @@ "ច្រាន​ចោល" "បន្ត" "ការកំណត់" + "%1$s កំពុង​ដំណើរការ" "អសកម្ម ពិនិត្យមើល​កម្មវិធី" "បញ្ហា កំពុងព្យាយាម​ម្ដងទៀត…" "រកមិន​ឃើញទេ" diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index b9fc90576391..2ecc0a138b81 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -1079,6 +1079,7 @@ "ವಜಾಗೊಳಿಸಿ" "ಪುನರಾರಂಭಿಸಿ" "ಸೆಟ್ಟಿಂಗ್‌ಗಳು" + "%1$s ರನ್ ಆಗುತ್ತಿದೆ" "ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ" "ದೋಷ, ಮರುಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ…" "ಕಂಡುಬಂದಿಲ್ಲ" diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index f5aadc85eab4..540fee6e651a 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -1079,6 +1079,7 @@ "닫기" "다시 시작" "설정" + "%1$s 실행 중" "비활성. 앱을 확인하세요." "오류 발생, 다시 시도 중…" "찾을 수 없음" diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index d1b6a09b4d67..4fa31fa2f561 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -1079,6 +1079,7 @@ "Жабуу" "Улантуу" "Жөндөөлөр" + "%1$s иштеп жатат" "Жигерсиз. Колдонмону текшериңиз" "Ката, дагы аракет жасалууда…" "Табылган жок" diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index daae298d0317..ef5f2e8d77fe 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -1079,6 +1079,7 @@ "ປິດໄວ້" "ສືບຕໍ່" "ການຕັ້ງຄ່າ" + "%1$s ກຳລັງເຮັດວຽກຢູ່" "ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ" "ຜິດພາດ, ກໍາລັງ​ລອງ​ໃໝ່…" "ບໍ່ພົບ" diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 5607584e7351..a4feb80d856d 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -1091,6 +1091,7 @@ "Atsisakyti" "Tęsti" "Nustatymai" + "„%1$s“ vykdoma" "Neaktyvu, patikrinkite progr." "Klaida, bandoma iš naujo…" "Nerasta" diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 7c64e26136bb..6d27691a82eb 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -1085,6 +1085,7 @@ "Nerādīt" "Atsākt" "Iestatījumi" + "Lietotne %1$s darbojas" "Neaktīva, pārbaudiet lietotni" "Radās kļūda. Mēģina vēlreiz…" "Netika atrasta" diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 4e7c06370b0f..5b91d4f753a6 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -1079,6 +1079,7 @@ "Отфрли" "Продолжи" "Поставки" + "%1$s работи" "Неактивна, провери апликација" "Грешка, повторен обид…" "Не е најдено" diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index dc07b609756d..6502e5666ea5 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -1079,6 +1079,7 @@ "ഡിസ്‌മിസ് ചെയ്യുക" "പുനരാരംഭിക്കുക" "ക്രമീകരണം" + "%1$s റൺ ചെയ്യുന്നു" "നിഷ്‌ക്രിയം, ആപ്പ് പരിശോധിക്കൂ" "പിശക്, വീണ്ടും ശ്രമിക്കുന്നു…" "കണ്ടെത്തിയില്ല" diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 4f85c057fd7b..cf6ec15572e5 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -1079,6 +1079,7 @@ "Хаах" "Үргэлжлүүлэх" "Тохиргоо" + "%1$s ажиллаж байна" "Идэвхгүй байна, аппыг шалгана уу" "Алдаа, дахин оролдож байна…" "Олдсонгүй" diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 1a608dae038f..17d874d4d2b8 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -1079,6 +1079,7 @@ "डिसमिस करा" "पुन्हा सुरू करा" "सेटिंग्ज" + "%1$s रन होत आहे" "निष्क्रिय, ॲप तपासा" "एरर, पुन्हा प्रयत्न करत आहे…" "आढळले नाही" diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index de2fcd817b7b..1eb74f5a85c8 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -1079,6 +1079,7 @@ "Tolak" "Sambung semula" "Tetapan" + "%1$s sedang dijalankan" "Tidak aktif, semak apl" "Ralat, mencuba semula…" "Tidak ditemukan" diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 07400bb4f180..bdda228802cd 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -1079,6 +1079,7 @@ "ပယ်ရန်" "ဆက်လုပ်ရန်" "ဆက်တင်များ" + "%1$s ပွင့်နေပါသည်" "ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ" "မှားသွားသည်၊ ပြန်စမ်းနေသည်…" "မတွေ့ပါ" diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index ead1fd5ab488..0561d6e45596 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -1079,6 +1079,7 @@ "Lukk" "Gjenoppta" "Innstillinger" + "%1$s kjører" "Inaktiv. Sjekk appen" "Feil. Prøver igjen …" "Ikke funnet" diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 4af7294beecf..f75a41b420cc 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -1079,6 +1079,7 @@ "हटाउनुहोस्" "सुचारु गर्नुहोस्" "सेटिङ" + "%1$s चलिरहेको छ" "निष्क्रिय छ, एप जाँच गर्नु…" "त्रुटि भयो, फेरि प्रयास गर्दै…" "फेला परेन" diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index bf2d757dd697..722e22683796 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -1079,6 +1079,7 @@ "Sluiten" "Hervatten" "Instellingen" + "%1$s is actief" "Inactief, check de app" "Fout. Opnieuw proberen…" "Niet gevonden" diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index fd1134960f1e..538e40647795 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -1079,6 +1079,7 @@ "ଖାରଜ କରନ୍ତୁ" "ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ" "ସେଟିଂସ୍" + "%1$s ଚାଲୁଛି" "ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ" "ତ୍ରୁଟି, ପୁଣି ଚେଷ୍ଟା କରୁଛି…" "ମିଳିଲା ନାହିଁ" diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 7da4635b77e9..59b2e883e833 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -1079,6 +1079,7 @@ "ਖਾਰਜ ਕਰੋ" "ਮੁੜ-ਚਾਲੂ ਕਰੋ" "ਸੈਟਿੰਗਾਂ" + "%1$s ਚੱਲ ਰਿਹਾ ਹੈ" "ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ" "ਗੜਬੜ, ਮੁੜ ਕੋਸ਼ਿਸ਼ ਹੋ ਰਹੀ ਹੈ…" "ਨਹੀਂ ਮਿਲਿਆ" diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 56ed3de4cd88..5bcee051f286 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1091,6 +1091,7 @@ "Odrzuć" "Wznów" "Ustawienia" + "Aplikacja %1$s jest uruchomiona" "Nieaktywny, sprawdź aplikację" "Błąd, próbuję jeszcze raz…" "Nie znaleziono" diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 860c372eec80..0012c2fe1786 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -1079,6 +1079,7 @@ "Dispensar" "Retomar" "Configurações" + "%1$s está em execução" "Inativo, verifique o app" "Erro. Tentando novamente…" "Não encontrado" diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index b491bd6b8f74..d918042d4fd5 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -1079,6 +1079,7 @@ "Ignorar" "Retomar" "Definições" + "%1$s em execução" "Inativa. Consulte a app." "Erro. A tentar novamente…" "Não encontrado." diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 860c372eec80..0012c2fe1786 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -1079,6 +1079,7 @@ "Dispensar" "Retomar" "Configurações" + "%1$s está em execução" "Inativo, verifique o app" "Erro. Tentando novamente…" "Não encontrado" diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 6046383df330..775d945aa226 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -1085,6 +1085,7 @@ "Închideți" "Reia" "Setări" + "%1$s rulează" "Inactiv, verificați aplicația" "Eroare, se încearcă din nou…" "Nu s-a găsit" diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 405cbdb22a48..62e3e23d79fe 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -1091,6 +1091,7 @@ "Скрыть" "Возобновить" "Настройки" + "Приложение \"%1$s\" запущено" "Нет ответа. Проверьте приложение." "Ошибка. Повторная попытка…" "Не найдено." diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 8c061fcea8ae..95dcc4e7bd24 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -1079,6 +1079,7 @@ "ඉවත ලන්න" "නැවත පටන් ගන්න" "සැකසීම්" + "%1$s ධාවනය වේ" "අක්‍රියයි, යෙදුම පරීක්ෂා කරන්න" "දෝෂයකි, නැවත උත්සාහ කරමින්…" "හමු නොවිණි" diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 4a29d87f44b0..6cc01dde73ac 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -1091,6 +1091,7 @@ "Zavrieť" "Pokračovať" "Nastavenia" + "Aplikácia %1$s je spustená" "Neaktívne, preverte aplikáciu" "Chyba, skúša sa znova…" "Nenájdené" diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index d5de4d3f0d30..220951a27c83 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -1091,6 +1091,7 @@ "Opusti" "Nadaljuj" "Nastavitve" + "%1$s se izvaja" "Neaktivno, poglejte aplikacijo" "Napaka, vnovični poskus …" "Ni mogoče najti" diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index de8090136376..bccdbbe62c3f 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -1079,6 +1079,7 @@ "Hiq" "Vazhdo" "Cilësimet" + "%1$s po ekzekutohet" "Joaktive, kontrollo aplikacionin" "Gabim, po provohet përsëri" "Nuk u gjet" diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 425ce213c1c3..2963464060e3 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -1085,6 +1085,7 @@ "Одбаци" "Настави" "Подешавања" + "Апликација %1$s је покренута" "Неактивно. Видите апликацију" "Грешка, покушава се поново…" "Није пронађено" diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 27de6d27b3f9..1c01285b64f0 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -1079,6 +1079,7 @@ "Stäng" "Återuppta" "Inställningar" + "%1$s körs" "Inaktiv, kolla appen" "Fel, försöker igen …" "Hittades inte" diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index fb1ae5569009..29ad94aab806 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -1079,6 +1079,7 @@ "Ondoa" "Endelea" "Mipangilio" + "%1$s inatumika" "Haitumiki, angalia programu" "Hitilafu, inajaribu tena…" "Hakipatikani" diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index e085e63d781f..bbf84562be81 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -1079,6 +1079,7 @@ "மூடுக" "தொடர்க" "அமைப்புகள்" + "%1$s இயங்கிக் கொண்டிருக்கிறது" "செயலில் இல்லை , சரிபார்க்கவும்" "பிழை, மீண்டும் முயல்கிறது…" "இல்லை" diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 865821f9f2a7..84d42870ee1a 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -1079,6 +1079,7 @@ "విస్మరించు" "కొనసాగించండి" "సెట్టింగ్‌లు" + "%1$s రన్ అవుతోంది" "ఇన్‌యాక్టివ్, యాప్ చెక్ చేయండి" "లోపం, మళ్లీ ప్రయత్నిస్తోంది..." "కనుగొనబడలేదు" diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index b542be31cd08..d288b5cfc0f4 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1079,6 +1079,7 @@ "ปิด" "เล่นต่อ" "การตั้งค่า" + "%1$s กำลังทำงาน" "ไม่มีการใช้งาน โปรดตรวจสอบแอป" "มีข้อผิดพลาด กำลังลองอีกครั้ง…" "ไม่พบ" diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 238fbc92b1ef..98754b1fcae4 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -1079,6 +1079,7 @@ "I-dismiss" "Ituloy" "Mga Setting" + "Tumatakbo ang %1$s" "Hindi aktibo, tingnan ang app" "Nagka-error, sinusubukan ulit…" "Hindi nahanap" diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 093f18f699a9..5f0bcece16a4 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -1079,6 +1079,7 @@ "Kapat" "Devam ettir" "Ayarlar" + "%1$s çalışıyor" "Devre dışı, uygulamaya bakın" "Hata, yeniden deneniyor…" "Bulunamadı" diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 52e41ff57059..55d892c9102c 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -1091,6 +1091,7 @@ "Закрити" "Відновити" "Налаштування" + "%1$s працює" "Неактивно, перейдіть у додаток" "Помилка. Повторна спроба…" "Не знайдено" diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 0a3853ecfc04..0041155be392 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -1079,6 +1079,7 @@ "برخاست کریں" "دوبارہ شروع کریں" "ترتیبات" + "%1$s چل رہی ہے" "غیر فعال، ایپ چیک کریں" "خرابی، دوبارہ کوشش کی جا رہی ہے…" "نہیں ملا" diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 59240ee1754a..58b4658a492c 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -1079,6 +1079,7 @@ "Yopish" "Davom etish" "Sozlamalar" + "%1$s ishlamoqda" "Nofaol. Ilovani tekshiring" "Xato, qayta urinilmoqda…" "Topilmadi" diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index e51036a745e3..9790b69ad52e 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -1079,6 +1079,7 @@ "Đóng" "Tiếp tục" "Cài đặt" + "%1$s đang chạy" "Không hoạt động, hãy kiểm tra ứng dụng" "Lỗi, đang thử lại…" "Không tìm thấy" diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index b42c89d30634..bd120fe34f6b 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -1079,6 +1079,7 @@ "关闭" "继续播放" "设置" + "“%1$s”正在运行" "无效,请检查应用" "出现错误,正在重试…" "未找到" diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index a1e7054c9faa..a5d8b58287cb 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -1079,6 +1079,7 @@ "關閉" "繼續播放" "設定" + "「%1$s」執行中" "已停用,請檢查應用程式" "發生錯誤,正在重試…" "找不到" diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index ed114c488fff..ef3129e97b70 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -1079,6 +1079,7 @@ "關閉" "繼續播放" "設定" + "「%1$s」執行中" "無效,請查看應用程式" "發生錯誤,正在重試…" "找不到控制項" diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index c8356fa25c11..d9448035837e 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -1079,6 +1079,7 @@ "Cashisa" "Qalisa kabusha" "Izilungiselelo" + "I-%1$s iyasebenza" "Akusebenzi, hlola uhlelo lokusebenza" "Iphutha, iyazama futhi…" "Ayitholakali" From 9d41d426ea34b0218fe1026bd3791871cb219465 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Mon, 8 May 2023 18:39:35 +0000 Subject: [PATCH 181/208] Verify URI permissions for EXTRA_REMOTE_INPUT_HISTORY_ITEMS. Also added a step to serialize & deserialize the notification in the test, to prevent exceptions about not being able to cast e.g. Parcelable[] to RemoteInputHistoryItem[]. Test: atest NotificationManagerServiceTest & tested with POC from bug Bug: 276729064 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:88e597d2b31d054ab5286b3a666accb08a8db5d5) Merged-In: I7053ca59f9c7f1df5226418594109cfb8b609b1e Change-Id: I7053ca59f9c7f1df5226418594109cfb8b609b1e --- core/java/android/app/Notification.java | 11 +++++++++++ .../NotificationManagerServiceTest.java | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ce2505591b38..5ae6621d5feb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2533,6 +2533,17 @@ public void visitUris(@NonNull Consumer visitor) { if (person != null && person.getIconUri() != null) { visitor.accept(person.getIconUri()); } + + final RemoteInputHistoryItem[] history = getParcelableArrayFromBundle(extras, + Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); + if (history != null) { + for (int i = 0; i < history.length; i++) { + RemoteInputHistoryItem item = history[i]; + if (item.getUri() != null) { + visitor.accept(item.getUri()); + } + } + } } if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index feaebe92a34e..31983b931108 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -102,6 +102,7 @@ import android.app.PendingIntent; import android.app.Person; import android.app.RemoteInput; +import android.app.RemoteInputHistoryItem; import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; @@ -4312,6 +4313,12 @@ public void testVisitUris() throws Exception { .setName("People List Person 2") .setIcon(personIcon3) .build(); + final Uri historyUri1 = Uri.parse("content://com.example/history1"); + final Uri historyUri2 = Uri.parse("content://com.example/history2"); + final RemoteInputHistoryItem historyItem1 = new RemoteInputHistoryItem(null, historyUri1, + "a"); + final RemoteInputHistoryItem historyItem2 = new RemoteInputHistoryItem(null, historyUri2, + "b"); Bundle extras = new Bundle(); extras.putParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI, audioContents); @@ -4319,6 +4326,8 @@ public void testVisitUris() throws Exception { extras.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person1); extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, new ArrayList<>(Arrays.asList(person2, person3))); + extras.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + new RemoteInputHistoryItem[]{historyItem1, historyItem2}); Notification n = new Notification.Builder(mContext, "a") .setContentTitle("notification with uris") @@ -4327,6 +4336,13 @@ public void testVisitUris() throws Exception { .addExtras(extras) .build(); + // Serialize and deserialize the notification to make sure nothing breaks in the process, + // since that's what will usually happen before we get to call visitUris. + Parcel parcel = Parcel.obtain(); + n.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + n = new Notification(parcel); + Consumer visitor = (Consumer) spy(Consumer.class); n.visitUris(visitor); verify(visitor, times(1)).accept(eq(audioContents)); @@ -4336,6 +4352,8 @@ public void testVisitUris() throws Exception { verify(visitor, times(1)).accept(eq(personIcon1.getUri())); verify(visitor, times(1)).accept(eq(personIcon2.getUri())); verify(visitor, times(1)).accept(eq(personIcon3.getUri())); + verify(visitor, times(1)).accept(eq(historyUri1)); + verify(visitor, times(1)).accept(eq(historyUri2)); } @Test From 28f202df4a29a7e7755e673a2702f6e572377ec9 Mon Sep 17 00:00:00 2001 From: Josep del Rio Date: Mon, 26 Jun 2023 11:16:37 +0000 Subject: [PATCH 182/208] Do not share key mappings with JNI object The key mapping information between the native key mappings and the KeyCharacterMap object available in Java is currently shared, which means that a read can be attempted while it's being modified. Because the code changed between R and S, this CL fixes it just for R; the patch for versions S+ is ag/23785419 Bug: 274058082 Test: Presubmit (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4b3c4620166071561ec44961fb08a56676b4fd6c) Merged-In: I3be94534dcda365da473f82347ae2e3f57bb1b42 Change-Id: I3be94534dcda365da473f82347ae2e3f57bb1b42 --- core/jni/android_view_InputDevice.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index 9f4e3e516ada..81ada6ad535c 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include @@ -48,9 +49,16 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi return NULL; } + sp map = deviceInfo.getKeyCharacterMap(); + if (map != nullptr) { + Parcel parcel; + map->writeToParcel(&parcel); + map = map->readFromParcel(&parcel); + } + ScopedLocalRef kcmObj(env, - android_view_KeyCharacterMap_create(env, deviceInfo.getId(), - deviceInfo.getKeyCharacterMap())); + android_view_KeyCharacterMap_create(env, deviceInfo.getId(), + map)); if (!kcmObj.get()) { return NULL; } From 2e881ad583cf68dae510b6e80add9150d558f520 Mon Sep 17 00:00:00 2001 From: Tim Yu Date: Tue, 20 Jun 2023 21:24:36 +0000 Subject: [PATCH 183/208] [DO NOT MERGE] Verify URI Permissions in Autofill RemoteViews Check permissions of URI inside of FillResponse's RemoteViews. If the current user does not have the required permissions to view the URI, the RemoteView is dropped from displaying. This fixes a security spill in which a user can view content of another user through a malicious Autofill provider. Bug: 283137865 Fixes: b/283264674 b/281666022 b/281665050 b/281848557 b/281533566 b/281534749 b/283101289 Test: Verified by POC app attached in bugs Test: atest CtsAutoFillServiceTestCases (added new tests) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:26beceb9a252a50374d056b162fa7e8ea55051b3) Merged-In: I6f4d2a35e89bbed7bd9e07bf5cd3e2d68b20af9a Change-Id: I6f4d2a35e89bbed7bd9e07bf5cd3e2d68b20af9a --- .../com/android/server/autofill/Helper.java | 45 +++++++++++++++++++ .../android/server/autofill/ui/FillUi.java | 11 +++-- .../android/server/autofill/ui/SaveUi.java | 3 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 80b0375a229d..8954a0c39091 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -18,6 +18,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; @@ -31,13 +33,18 @@ import android.view.WindowManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import android.widget.RemoteViews; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; + +import java.util.Arrays; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + public final class Helper { @@ -71,6 +78,44 @@ private Helper() { throw new UnsupportedOperationException("contains static members only"); } + private static boolean checkRemoteViewUriPermissions( + @UserIdInt int userId, @NonNull RemoteViews rView) { + final AtomicBoolean permissionsOk = new AtomicBoolean(true); + + rView.visitUris(uri -> { + int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri); + boolean allowed = uriOwnerId == userId; + permissionsOk.set(allowed && permissionsOk.get()); + }); + + return permissionsOk.get(); + } + + /** + * Checks the URI permissions of the remote view, + * to see if the current userId is able to access it. + * + * Returns the RemoteView that is passed if user is able, null otherwise. + * + * TODO: instead of returning a null remoteview when + * the current userId cannot access an URI, + * return a new RemoteView with the URI removed. + */ + public static @Nullable RemoteViews sanitizeRemoteView(RemoteViews rView) { + if (rView == null) return null; + + int userId = ActivityManager.getCurrentUser(); + + boolean ok = checkRemoteViewUriPermissions(userId, rView); + if (!ok) { + Slog.w(TAG, + "sanitizeRemoteView() user: " + userId + + " tried accessing resource that does not belong to them"); + } + return (ok ? rView : null); + } + + @Nullable static AutofillId[] toArray(@Nullable ArraySet set) { if (set == null) return null; diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 890208720f97..b35fd92e366d 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -141,8 +141,9 @@ public static boolean isFullScreen(Context context) { final LayoutInflater inflater = LayoutInflater.from(mContext); - final RemoteViews headerPresentation = response.getHeader(); - final RemoteViews footerPresentation = response.getFooter(); + final RemoteViews headerPresentation = Helper.sanitizeRemoteView(response.getHeader()); + final RemoteViews footerPresentation = Helper.sanitizeRemoteView(response.getFooter()); + final ViewGroup decor; if (mFullScreen) { decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null); @@ -220,6 +221,9 @@ public static boolean isFullScreen(Context context) { ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker); final View content; try { + if (Helper.sanitizeRemoteView(response.getPresentation()) == null) { + throw new RuntimeException("Permission error accessing RemoteView"); + } content = response.getPresentation().applyWithTheme( mContext, decor, interceptionHandler, mThemeId); container.addView(content); @@ -298,7 +302,8 @@ public static boolean isFullScreen(Context context) { final Dataset dataset = response.getDatasets().get(i); final int index = dataset.getFieldIds().indexOf(focusedViewId); if (index >= 0) { - final RemoteViews presentation = dataset.getFieldPresentation(index); + final RemoteViews presentation = Helper.sanitizeRemoteView( + dataset.getFieldPresentation(index)); if (presentation == null) { Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because " + "service didn't provide a presentation for it on " + dataset); diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 1c3116699b2d..7b45ef881e94 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -361,8 +361,7 @@ private boolean applyCustomDescription(@NonNull Context context, @NonNull View s return false; } writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION); - - final RemoteViews template = customDescription.getPresentation(); + final RemoteViews template = Helper.sanitizeRemoteView(customDescription.getPresentation()); if (template == null) { Slog.w(TAG, "No remote view on custom description"); return false; From 3c6166495f7eaa4638c7b8565698763f57706b01 Mon Sep 17 00:00:00 2001 From: Josep del Rio Date: Wed, 12 Jul 2023 16:32:05 +0000 Subject: [PATCH 184/208] Fix KCM key mapping cloning ag/23792288 tried to fix a security issue by cloning the key mappings, but unfortunately the parcel was not being rewinded. Bug: 274058082 Test: Confirmed change works in newer Android versions (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:aaaba6cf190d976efdc5db6c78997dbdc9214c15) Merged-In: I6f75b9202e20d82ebf81a35a2916e653ee1b8372 Change-Id: I6f75b9202e20d82ebf81a35a2916e653ee1b8372 --- core/jni/android_view_InputDevice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index 81ada6ad535c..87102d3359a7 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -53,6 +53,7 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi if (map != nullptr) { Parcel parcel; map->writeToParcel(&parcel); + parcel.setDataPosition(0); map = map->readFromParcel(&parcel); } From 341e878eae64277e8c7cbd9c9386f8ea554906b6 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 24 May 2023 19:35:44 -0700 Subject: [PATCH 185/208] Disallow loading icon from content URI to PipMenu Bug: 278246904 Test: manually, with the PoC app attached to the bug (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5f5a87d8a0dc9190327ba0e6113d5b80ee96abae) Merged-In: Iecfc1fb962de611cbe3c51a44ba4fded53925a7d Change-Id: Iecfc1fb962de611cbe3c51a44ba4fded53925a7d --- .../systemui/pip/phone/PipMenuActivity.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 586399c6dfd5..9771db986b29 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -53,6 +53,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -560,13 +561,17 @@ private void updateActionViews(Rect stackBounds) { final RemoteAction action = mActions.get(i); final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i); - // TODO: Check if the action drawable has changed before we reload it - action.getIcon().loadDrawableAsync(this, d -> { - if (d != null) { - d.setTint(Color.WHITE); - actionView.setImageDrawable(d); - } - }, mHandler); + final int iconType = action.getIcon().getType(); + if (iconType == Icon.TYPE_URI || iconType == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // Disallow loading icon from content URI + actionView.setImageDrawable(null); + } else { + // TODO: Check if the action drawable has changed before we reload it + action.getIcon().loadDrawableAsync(this, d -> { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + }, mHandler); + } actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { actionView.setOnClickListener(v -> { From 113637589027b9958302991c51ab210196da9293 Mon Sep 17 00:00:00 2001 From: Kunal Malhotra Date: Fri, 2 Jun 2023 23:32:02 +0000 Subject: [PATCH 186/208] Fixing DatabaseUtils to detect malformed UTF-16 strings Test: tested with POC in bug, also using atest Bug: 224771621 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:fb4a72e3943d166088407e61aa4439ac349f3f12) Merged-In: Ide65205b83063801971c5778af3154bcf3f0e530 Change-Id: Ide65205b83063801971c5778af3154bcf3f0e530 --- core/java/android/database/DatabaseUtils.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index b978ae559390..35c8dad09d60 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -511,17 +511,31 @@ public static void cursorFillWindow(final Cursor cursor, */ public static void appendEscapedSQLString(StringBuilder sb, String sqlString) { sb.append('\''); - if (sqlString.indexOf('\'') != -1) { - int length = sqlString.length(); - for (int i = 0; i < length; i++) { - char c = sqlString.charAt(i); - if (c == '\'') { - sb.append('\''); + int length = sqlString.length(); + for (int i = 0; i < length; i++) { + char c = sqlString.charAt(i); + if (Character.isHighSurrogate(c)) { + if (i == length - 1) { + continue; + } + if (Character.isLowSurrogate(sqlString.charAt(i + 1))) { + // add them both + sb.append(c); + sb.append(sqlString.charAt(i + 1)); + continue; + } else { + // this is a lone surrogate, skip it + continue; } - sb.append(c); } - } else - sb.append(sqlString); + if (Character.isLowSurrogate(c)) { + continue; + } + if (c == '\'') { + sb.append('\''); + } + sb.append(c); + } sb.append('\''); } From 87c8caf37fcbdfd748477c212440da833542dfce Mon Sep 17 00:00:00 2001 From: Anton Potapov Date: Tue, 4 Jul 2023 12:15:41 +0100 Subject: [PATCH 187/208] Add userId check before loading icon in Device Controls Test: manual with the steps from the bug Test: manual with a normal icon Test: atest CanUseIconPredicate Test: atest ControlViewHolderTest Bug: 272025416 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ffa97f42dd9496bb404e01727c923292d05a4466) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:331f2f1baaf7b62e90a7d7cb6d4481925b3d9071) Merged-In: I354469a53611c094c7bb695b1c2017c6786dd396 Change-Id: I354469a53611c094c7bb695b1c2017c6786dd396 --- .../controls/management/ControlAdapter.kt | 11 ++- .../management/ControlsEditingActivity.kt | 2 +- .../management/ControlsFavoritingActivity.kt | 5 +- .../controls/management/StructureAdapter.kt | 11 +-- .../controls/ui/CanUseIconPredicate.kt | 30 +++++++++ .../systemui/controls/ui/ControlViewHolder.kt | 8 ++- .../controls/ui/ControlsUiControllerImpl.kt | 4 +- .../controls/ui/CanUseIconPredicateTest.kt | 67 +++++++++++++++++++ 8 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 40662536e57e..45916d6e24ab 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -36,6 +36,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.android.systemui.R import com.android.systemui.controls.ControlInterface +import com.android.systemui.controls.ui.CanUseIconPredicate import com.android.systemui.controls.ui.RenderInfo private typealias ModelFavoriteChanger = (String, Boolean) -> Unit @@ -49,7 +50,8 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit * @property elevation elevation of each control view */ class ControlAdapter( - private val elevation: Float + private val elevation: Float, + private val currentUserId: Int ) : RecyclerView.Adapter() { companion object { @@ -84,6 +86,7 @@ class ControlAdapter( background = parent.context.getDrawable( R.drawable.control_background_ripple) }, + currentUserId, model?.moveHelper // Indicates that position information is needed ) { id, favorite -> model?.changeFavoriteStatus(id, favorite) @@ -189,6 +192,7 @@ private class ZoneHolder(view: View) : Holder(view) { */ internal class ControlHolder( view: View, + currentUserId: Int, val moveHelper: ControlsModel.MoveHelper?, val favoriteCallback: ModelFavoriteChanger ) : Holder(view) { @@ -205,6 +209,7 @@ internal class ControlHolder( visibility = View.VISIBLE } + private val canUseIconPredicate = CanUseIconPredicate(currentUserId) private val accessibilityDelegate = ControlHolderAccessibilityDelegate( this::stateDescription, this::getLayoutPosition, @@ -264,7 +269,9 @@ internal class ControlHolder( val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) icon.imageTintList = null - ci.customIcon?.let { + ci.customIcon + ?.takeIf(canUseIconPredicate) + ?.let { icon.setImageIcon(it) } ?: run { icon.setImageDrawable(ri.icon) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt index f68388d5db3f..81ad7d27c4b3 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt @@ -176,7 +176,7 @@ class ControlsEditingActivity @Inject constructor( val elevation = resources.getFloat(R.dimen.control_card_elevation) val recyclerView = requireViewById(R.id.list) recyclerView.alpha = 0.0f - val adapter = ControlAdapter(elevation).apply { + val adapter = ControlAdapter(elevation, currentUserTracker.currentUserId).apply { registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { var hasAnimated = false override fun onChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index b2821579c389..ca06b4ef8843 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -163,7 +163,8 @@ class ControlsFavoritingActivity @Inject constructor( } executor.execute { - structurePager.adapter = StructureAdapter(listOfStructures) + structurePager.adapter = StructureAdapter(listOfStructures, + currentUserTracker.currentUserId) structurePager.setCurrentItem(structureIndex) if (error) { statusText.text = resources.getString(R.string.controls_favorite_load_error, @@ -209,7 +210,7 @@ class ControlsFavoritingActivity @Inject constructor( structurePager.alpha = 0.0f pageIndicator.alpha = 0.0f structurePager.apply { - adapter = StructureAdapter(emptyList()) + adapter = StructureAdapter(emptyList(), currentUserTracker.currentUserId) registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt index cb67454195ec..7524f1cc2226 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt @@ -24,13 +24,15 @@ import androidx.recyclerview.widget.RecyclerView import com.android.systemui.R class StructureAdapter( - private val models: List + private val models: List, + private val currentUserId: Int ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder { val layoutInflater = LayoutInflater.from(parent.context) return StructureHolder( - layoutInflater.inflate(R.layout.controls_structure_page, parent, false) + layoutInflater.inflate(R.layout.controls_structure_page, parent, false), + currentUserId ) } @@ -40,7 +42,8 @@ class StructureAdapter( holder.bind(models[index].model) } - class StructureHolder(view: View) : RecyclerView.ViewHolder(view) { + class StructureHolder(view: View, currentUserId: Int) : + RecyclerView.ViewHolder(view) { private val recyclerView: RecyclerView private val controlAdapter: ControlAdapter @@ -48,7 +51,7 @@ class StructureAdapter( init { recyclerView = itemView.requireViewById(R.id.listAll) val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation) - controlAdapter = ControlAdapter(elevation) + controlAdapter = ControlAdapter(elevation, currentUserId) setUpRecyclerView() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt new file mode 100644 index 000000000000..61c21237144d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.content.ContentProvider +import android.graphics.drawable.Icon + +class CanUseIconPredicate(private val currentUserId: Int) : (Icon) -> Boolean { + + override fun invoke(icon: Icon): Boolean = + if (icon.type == Icon.TYPE_URI || icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + ContentProvider.getUserIdFromUri(icon.uri, currentUserId) == currentUserId + } else { + true + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 0e4f68431c16..e69524da0fb6 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -62,7 +62,8 @@ class ControlViewHolder( val controlsController: ControlsController, val uiExecutor: DelayableExecutor, val bgExecutor: DelayableExecutor, - val controlActionCoordinator: ControlActionCoordinator + val controlActionCoordinator: ControlActionCoordinator, + val currentUserId: Int ) { companion object { @@ -99,6 +100,7 @@ class ControlViewHolder( } } + private val canUseIconPredicate = CanUseIconPredicate(currentUserId) private val toggleBackgroundIntensity: Float = layout.context.resources .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1) private var stateAnimator: ValueAnimator? = null @@ -411,7 +413,9 @@ class ControlViewHolder( status.setTextColor(color) - control?.getCustomIcon()?.let { + control?.getCustomIcon() + ?.takeIf(canUseIconPredicate) + ?.let { // do not tint custom icons, assume the intended icon color is correct if (icon.imageTintList != null) { icon.imageTintList = null diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5a525974f3cb..17453230310f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -400,8 +400,8 @@ class ControlsUiControllerImpl @Inject constructor ( controlsController.get(), uiExecutor, bgExecutor, - controlActionCoordinator - ) + controlActionCoordinator, + controlsController.get().currentUserId) cvh.bindData(it) controlViewsById.put(key, cvh) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt new file mode 100644 index 000000000000..ed17f179eeb0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.graphics.Bitmap +import android.graphics.drawable.Icon +import android.net.Uri +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class CanUseIconPredicateTest : SysuiTestCase() { + + private companion object { + const val USER_ID_1 = 1 + const val USER_ID_2 = 2 + } + + val underTest: CanUseIconPredicate = CanUseIconPredicate(USER_ID_1) + + @Test + fun testReturnsFalseForDifferentUser() { + val user2Icon = Icon.createWithContentUri("content://$USER_ID_2@test") + + assertThat(underTest.invoke(user2Icon)).isFalse() + } + + @Test + fun testReturnsTrueForCorrectUser() { + val user1Icon = Icon.createWithContentUri("content://$USER_ID_1@test") + + assertThat(underTest.invoke(user1Icon)).isTrue() + } + + @Test + fun testReturnsTrueForUriWithoutUser() { + val uriIcon = Icon.createWithContentUri(Uri.parse("content://test")) + + assertThat(underTest.invoke(uriIcon)).isTrue() + } + + @Test + fun testReturnsTrueForNonUriIcon() { + val bitmapIcon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)) + + assertThat(underTest.invoke(bitmapIcon)).isTrue() + } +} From f9b271182957f590a95aa2272bc8854bd8a3538f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 28 Jul 2023 22:03:03 +0000 Subject: [PATCH 188/208] RESTRICT AUTOMERGE: SettingsProvider: exclude secure_frp_mode from resets When RescueParty detects that a system process is crashing frequently, it tries to recover in various ways, such as by resetting all settings. Unfortunately, this included resetting the secure_frp_mode setting, which is the means by which the system keeps track of whether the Factory Reset Protection (FRP) challenge has been passed yet. With this setting reset, some FRP restrictions went away and it became possible to bypass FRP by setting a new lockscreen credential. Fix this by excluding secure_frp_mode from resets. Note: currently this bug isn't reproducible on 'main' due to ag/23727749 disabling much of RescueParty, but that is a temporary change. Bug: 253043065 Test: With ag/23727749 reverted and with my fix to prevent com.android.settings from crashing *not* applied, tried repeatedly setting lockscreen credential while in FRP mode, using the smartlock setup activity launched by intent via adb. Verified that although RescueParty is still triggered after 5 attempts, secure_frp_mode is no longer reset (its value remains "1"). Test: Verified that secure_frp_mode still gets changed from 1 to 0 when FRP is passed legitimately. Test: atest com.android.providers.settings.SettingsProviderTest Test: atest android.provider.SettingsProviderTest (cherry picked from commit 9890dd7f15c091f7d1a09e4fddb9f85d32015955) (changed Global.SECURE_FRP_MODE to Secure.SECURE_FRP_MODE, needed because this setting was moved in U) (removed static keyword from shouldExcludeSettingFromReset(), needed for compatibility with Java 15 and earlier) (resolved conflict in resetSettingsLocked()) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f0f020c21fd29d8076733e07847e6314172a312e) Merged-In: Id95ed43b9cc2208090064392bcd5dc012710af93 Change-Id: Id95ed43b9cc2208090064392bcd5dc012710af93 --- .../providers/settings/SettingsProvider.java | 17 ++++++++++--- .../settings/SettingsProviderTest.java | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index b95d34f2966b..9561f2cec96d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2955,6 +2955,15 @@ public Setting getSettingLocked(int type, int userId, String name) { return settingsState.getSettingLocked(name); } + private boolean shouldExcludeSettingFromReset(Setting setting, String prefix) { + // If a prefix was specified, exclude settings whose names don't start with it. + if (prefix != null && !setting.getName().startsWith(prefix)) { + return true; + } + // Never reset SECURE_FRP_MODE, as it could be abused to bypass FRP via RescueParty. + return Secure.SECURE_FRP_MODE.equals(setting.getName()); + } + public void resetSettingsLocked(int type, int userId, String packageName, int mode, String tag) { resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/ @@ -2977,7 +2986,7 @@ public void resetSettingsLocked(int type, int userId, String packageName, int mo Setting setting = settingsState.getSettingLocked(name); if (packageName.equals(setting.getPackageName())) { if ((tag != null && !tag.equals(setting.getTag())) - || (prefix != null && !setting.getName().startsWith(prefix))) { + || shouldExcludeSettingFromReset(setting, prefix)) { continue; } if (settingsState.resetSettingLocked(name)) { @@ -2997,7 +3006,7 @@ public void resetSettingsLocked(int type, int userId, String packageName, int mo Setting setting = settingsState.getSettingLocked(name); if (!SettingsState.isSystemPackage(getContext(), setting.getPackageName(), INVALID_UID, userId)) { - if (prefix != null && !setting.getName().startsWith(prefix)) { + if (shouldExcludeSettingFromReset(setting, prefix)) { continue; } if (settingsState.resetSettingLocked(name)) { @@ -3017,7 +3026,7 @@ public void resetSettingsLocked(int type, int userId, String packageName, int mo Setting setting = settingsState.getSettingLocked(name); if (!SettingsState.isSystemPackage(getContext(), setting.getPackageName(), INVALID_UID, userId)) { - if (prefix != null && !setting.getName().startsWith(prefix)) { + if (shouldExcludeSettingFromReset(setting, prefix)) { continue; } if (setting.isDefaultFromSystem()) { @@ -3040,7 +3049,7 @@ public void resetSettingsLocked(int type, int userId, String packageName, int mo for (String name : settingsState.getSettingNamesLocked()) { Setting setting = settingsState.getSettingLocked(name); boolean someSettingChanged = false; - if (prefix != null && !setting.getName().startsWith(prefix)) { + if (shouldExcludeSettingFromReset(setting, prefix)) { continue; } if (setting.isDefaultFromSystem()) { diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java index 8ff595b3bc53..0fa0f728d4ce 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java @@ -463,6 +463,31 @@ private void testResetModeTrustedDefaultsCommon(int type) throws Exception { } } + // To prevent FRP bypasses, the SECURE_FRP_MODE setting should not be reset when all other + // settings are reset. But it should still be possible to explicitly set its value. + @Test + public void testSecureFrpModeSettingCannotBeReset() throws Exception { + final String name = Settings.Secure.SECURE_FRP_MODE; + final String origValue = getSetting(SETTING_TYPE_GLOBAL, name); + setSettingViaShell(SETTING_TYPE_GLOBAL, name, "1", false); + try { + assertEquals("1", getSetting(SETTING_TYPE_GLOBAL, name)); + for (int type : new int[] { SETTING_TYPE_GLOBAL, SETTING_TYPE_SECURE }) { + resetSettingsViaShell(type, Settings.RESET_MODE_UNTRUSTED_DEFAULTS); + resetSettingsViaShell(type, Settings.RESET_MODE_UNTRUSTED_CHANGES); + resetSettingsViaShell(type, Settings.RESET_MODE_TRUSTED_DEFAULTS); + } + // The value should still be "1". It should not have been reset to null. + assertEquals("1", getSetting(SETTING_TYPE_GLOBAL, name)); + // It should still be possible to explicitly set the value to "0". + setSettingViaShell(SETTING_TYPE_GLOBAL, name, "0", false); + assertEquals("0", getSetting(SETTING_TYPE_GLOBAL, name)); + } finally { + setSettingViaShell(SETTING_TYPE_GLOBAL, name, origValue, false); + assertEquals(origValue, getSetting(SETTING_TYPE_GLOBAL, name)); + } + } + private void doTestQueryStringInBracketsViaProviderApiForType(int type) { // Make sure we have a clean slate. deleteStringViaProviderApi(type, FAKE_SETTING_NAME); From 09e783a2d107c7dcee4d7e662485bcc93dba307f Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 31 Jul 2023 14:05:36 -0700 Subject: [PATCH 189/208] [RESTRICT AUTOMERGE] Ignore small source rect hint Which may be abused by malicious app to create a non-visible PiP window that bypasses the background restriction. Bug: 270368476 Test: Manually, using the POC app (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a54d763886ffd69aa14360dc999c76cd2af263f2) Merged-In: Ifc0e4ffe8b7a9754053246069cb480aa6a59a7e1 Change-Id: Ifc0e4ffe8b7a9754053246069cb480aa6a59a7e1 --- .../com/android/systemui/pip/PipTaskOrganizer.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 29d77a72c73a..2e9455d5e0d7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -442,7 +442,8 @@ mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds); + final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds, + destinationBounds); scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); @@ -457,14 +458,17 @@ mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), /** * Returns the source hint rect if it is valid (if provided and is contained by the current - * task bounds). + * task bounds and not too small). */ - private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds) { + private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds, + Rect destinationBounds) { final Rect sourceHintRect = info.pictureInPictureParams != null && info.pictureInPictureParams.hasSourceBoundsHint() ? info.pictureInPictureParams.getSourceRectHint() : null; - if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) { + if (sourceHintRect != null && sourceBounds.contains(sourceHintRect) + && sourceHintRect.width() > destinationBounds.width() + && sourceHintRect.height() > destinationBounds.height()) { return sourceHintRect; } return null; From 019db419db25c41dbf95e1e797f856f5f45e95f1 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Thu, 10 Aug 2023 15:37:24 +0000 Subject: [PATCH 190/208] Revert "DO NOT MERGE Dismiss keyguard when simpin auth'd and..." This reverts commit 09f004722284ef6b9790ddf9338a1708b3f0833c. Reason for revert: causing a partner bug Fixes: 295205456 Bug: 222446076 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8df038265475bb062ead9eec1749ee92a0f5eb4e) Merged-In: Ida11d98117727f63547b096617a4778bea429e22 Change-Id: Ida11d98117727f63547b096617a4778bea429e22 --- .../src/com/android/keyguard/KeyguardSecurityContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 1cef61a2e18a..b2020d9fe2d5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -739,7 +739,7 @@ boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, case SimPuk: // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled( + if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser())) { finish = true; eventSubtype = BOUNCER_DISMISS_SIM; From cd73140b2cf09e45b9289be58af2775dae2828a9 Mon Sep 17 00:00:00 2001 From: Nan Wu Date: Fri, 16 Jun 2023 14:42:24 +0000 Subject: [PATCH 191/208] DO NOT MERGE Fix BAL via notification.publicVersion We stripped the token that allows app to retrieve their own notification and fire their own PI to launch activities from background. But we forgot to strip the token from notification.publicVersion Bug: 278558814 Test: NotificationManagerTest#testActivityStartFromRetrievedNotification_isBlocked (cherry picked from commit cf851d81a954f0a6dd0c2fd7defa93932539e7f9) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1896c2e7068c9ec1ab8355d863d7e8107d5d5706) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:75fcbb37617246c43c2af34b12c9ae4b4043f9ac) Merged-In: I8f25d7a5e47890a0496af023149717e1df482f98 Change-Id: I8f25d7a5e47890a0496af023149717e1df482f98 --- core/java/android/app/Notification.java | 7 +++++-- .../server/notification/NotificationManagerService.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 5ae6621d5feb..d0392abd35f9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3069,8 +3069,11 @@ public void setLatestEventInfo(Context context, * * @hide */ - public void setAllowlistToken(@Nullable IBinder token) { - mWhitelistToken = token; + public void clearAllowlistToken() { + mWhitelistToken = null; + if (publicVersion != null) { + publicVersion.clearAllowlistToken(); + } } /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index caf7d70edb09..a20ba696df94 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3992,7 +3992,7 @@ private StatusBarNotification sanitizeSbn(String pkg, int userId, // Remove background token before returning notification to untrusted app, this // ensures the app isn't able to perform background operations that are // associated with notification interactions. - notification.setAllowlistToken(null); + notification.clearAllowlistToken(); return new StatusBarNotification( sbn.getPackageName(), sbn.getOpPkg(), From eb65225d5d0365e0da93663d76a23dcde12e17ee Mon Sep 17 00:00:00 2001 From: Piyush Mehrotra Date: Thu, 27 Jul 2023 19:35:14 +0000 Subject: [PATCH 192/208] [DO NOT MERGE] Check caller's uid in backupAgentCreated callback AM.backupAgentCreated() should enforce that caller belongs the package called in the API. Bug: 289549315 Test: atest android.security.cts.ActivityManagerTest#testActivityManager_backupAgentCreated_rejectIfCallerUidNotEqualsPackageUid (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:52b91363583c4e2b68f1a818b067cefe04809285) Merged-In: I9f3ae5ec0b8f00e020d471cc0eddf8bd8bdbb82d Change-Id: I9f3ae5ec0b8f00e020d471cc0eddf8bd8bdbb82d --- .../server/am/ActivityManagerService.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1cd202dce438..bd2e9e982106 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3272,6 +3272,22 @@ CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) { } } + /** + * Enforces that the uid of the caller matches the uid of the package. + * + * @param packageName the name of the package to match uid against. + * @param callingUid the uid of the caller. + * @throws SecurityException if the calling uid doesn't match uid of the package. + */ + private void enforceCallingPackage(String packageName, int callingUid) { + final int userId = UserHandle.getUserId(callingUid); + final int packageUid = getPackageManagerInternalLocked().getPackageUid(packageName, + /*flags=*/ 0, userId); + if (packageUid != callingUid) { + throw new SecurityException(packageName + " does not belong to uid " + callingUid); + } + } + @Override public void setPackageScreenCompatMode(String packageName, int mode) { mActivityTaskManager.setPackageScreenCompatMode(packageName, mode); @@ -15450,13 +15466,16 @@ private void clearPendingBackup(int userId) { // A backup agent has just come up @Override public void backupAgentCreated(String agentPackageName, IBinder agent, int userId) { + final int callingUid = Binder.getCallingUid(); + enforceCallingPackage(agentPackageName, callingUid); + // Resolve the target user id and enforce permissions. - userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, /* allowAll */ false, ALLOW_FULL_ONLY, "backupAgentCreated", null); if (DEBUG_BACKUP) { Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName + " = " + agent + " callingUserId = " + UserHandle.getCallingUserId() + " userId = " + userId - + " callingUid = " + Binder.getCallingUid() + " uid = " + Process.myUid()); + + " callingUid = " + callingUid + " uid = " + Process.myUid()); } synchronized(this) { From 1683b61273aca124186c81427445e045ffdbfc59 Mon Sep 17 00:00:00 2001 From: kumarashishg Date: Thu, 3 Aug 2023 12:01:29 +0000 Subject: [PATCH 193/208] Use type safe API of readParcelableArray Bug: 291299076 Test: Build and flash the device and check if it throws exception for non UsbInterface object Test: atest CtsUsbManagerTestCases (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:85d7e6712a9eeeed3bdd68ea3c3862c7e88bfe70) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:60bfbde79f2ffb012abced55d358fdf6380c0bae) Merged-In: I2917c8331b6d56caaa9a6479bcd9a2d089f5f503 Change-Id: I2917c8331b6d56caaa9a6479bcd9a2d089f5f503 --- core/java/android/hardware/usb/UsbConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/java/android/hardware/usb/UsbConfiguration.java b/core/java/android/hardware/usb/UsbConfiguration.java index 66269cb772f8..b25f47b11532 100644 --- a/core/java/android/hardware/usb/UsbConfiguration.java +++ b/core/java/android/hardware/usb/UsbConfiguration.java @@ -172,7 +172,8 @@ public UsbConfiguration createFromParcel(Parcel in) { String name = in.readString(); int attributes = in.readInt(); int maxPower = in.readInt(); - Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader()); + Parcelable[] interfaces = in.readParcelableArray( + UsbInterface.class.getClassLoader(), UsbInterface.class); UsbConfiguration configuration = new UsbConfiguration(id, name, attributes, maxPower); configuration.setInterfaces(interfaces); return configuration; From e4ca53b9f6220d9f6c465f099627775624f28c9f Mon Sep 17 00:00:00 2001 From: Vova Sharaienko Date: Mon, 24 Jul 2023 23:19:34 +0000 Subject: [PATCH 194/208] RESTRICT AUTOMERGE Make log reader thread a class member pushedEventThread references class members after detaching. Making pushedEventThread as class member and joining in statsService destructor. Adding a method to stop readLogs thread. Ignore-AOSP-First: Bug is in still security triage and fuzzer is crashing on startup. Test: atest statsd_test Test: m statsd_service_fuzzer && adb sync data && adb shell /data/fuzz/arm64/statsd_service_fuzzer/statsd_service_fuzzer -runs=10000 Bug: 285645039 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:03de4e4f1a0546fdd3b002651851bee9ffe0e11b) Merged-In: I1e886f9ccb7203714216da061c35e793b2a63d8a Change-Id: I1e886f9ccb7203714216da061c35e793b2a63d8a --- cmds/statsd/src/StatsService.cpp | 23 +++++++++++++++++++++-- cmds/statsd/src/StatsService.h | 8 ++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 6f952f637506..8ec668a6c5b5 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -163,12 +163,15 @@ StatsService::StatsService(const sp& handlerLooper, shared_ptr([this] { readLogs(); }); } } StatsService::~StatsService() { + if (mEventQueue != nullptr) { + stopReadingLogs(); + mLogsReaderThread->join(); + } } /* Runs on a dedicated thread to process pushed events. */ @@ -177,6 +180,13 @@ void StatsService::readLogs() { while (1) { // Block until an event is available. auto event = mEventQueue->waitPop(); + + // Below flag will be set when statsd is exiting and log event will be pushed to break + // out of waitPop. + if (mIsStopRequested) { + break; + } + // Pass it to StatsLogProcess to all configs/metrics // At this point, the LogEventQueue is not blocked, so that the socketListener // can read events from the socket and write to buffer to avoid data drop. @@ -1335,6 +1345,15 @@ void StatsService::statsCompanionServiceDiedImpl() { mPullerManager->SetStatsCompanionService(nullptr); } +void StatsService::stopReadingLogs() { + mIsStopRequested = true; + // Push this event so that readLogs will process and break out of the loop + // after the stop is requested. + int64_t timeStamp; + std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); + mEventQueue->push(std::move(logEvent), &timeStamp); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index b49fa1d42e66..a12d82eedca9 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -338,6 +338,13 @@ class StatsService : public BnStatsd { */ void statsCompanionServiceDiedImpl(); + /* + * This method is used to stop log reader thread. + */ + void stopReadingLogs(); + + std::atomic mIsStopRequested = false; + /** * Tracks the uid <--> package name mapping. */ @@ -380,6 +387,7 @@ class StatsService : public BnStatsd { */ mutable mutex mShellSubscriberMutex; std::shared_ptr mEventQueue; + std::unique_ptr mLogsReaderThread; MultiConditionTrigger mBootCompleteTrigger; static const inline string kBootCompleteTag = "BOOT_COMPLETE"; From f8d761d99eb4db78c35490d77f7ac072b6b52efc Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Mon, 14 Aug 2023 15:24:11 -0700 Subject: [PATCH 195/208] [SettingsProvider] verify ringtone URI before setting Similar to ag/24422287, but the same URI verification should be done in SettingsProvider as well, which can be called by apps via Settings.System API or ContentProvider APIs without using RingtoneManager. BUG: 227201030 Test: manual with a test app. Will add a CTS test. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1b234678ec122994ccbfc52ac48aafdad7fdb1ed) Merged-In: Ic0ffa1db14b5660d02880b632a7f2ad9e6e5d84b Change-Id: Ic0ffa1db14b5660d02880b632a7f2ad9e6e5d84b --- .../providers/settings/SettingsProvider.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 9561f2cec96d..d3c10574ea13 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1781,6 +1781,9 @@ private boolean mutateSystemSetting(String name, String value, int runAsUserId, cacheName = Settings.System.ALARM_ALERT_CACHE; } if (cacheName != null) { + if (!isValidAudioUri(name, value)) { + return false; + } final File cacheFile = new File( getRingtoneCacheDir(owningUserId), cacheName); cacheFile.delete(); @@ -1813,6 +1816,34 @@ owningUserId, name, value, null, false, getCallingPackage(), } } + private boolean isValidAudioUri(String name, String uri) { + if (uri != null) { + Uri audioUri = Uri.parse(uri); + if (Settings.AUTHORITY.equals( + ContentProvider.getAuthorityWithoutUserId(audioUri.getAuthority()))) { + // Don't accept setting the default uri to self-referential URIs like + // Settings.System.DEFAULT_RINGTONE_URI, which is an alias to the value of this + // setting. + return false; + } + final String mimeType = getContext().getContentResolver().getType(audioUri); + if (mimeType == null) { + Slog.e(LOG_TAG, + "mutateSystemSetting for setting: " + name + " URI: " + audioUri + + " ignored: failure to find mimeType (no access from this context?)"); + return false; + } + if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg") + || mimeType.equals("application/x-flac"))) { + Slog.e(LOG_TAG, + "mutateSystemSetting for setting: " + name + " URI: " + audioUri + + " ignored: associated mimeType: " + mimeType + " is not an audio type"); + return false; + } + } + return true; + } + private boolean hasWriteSecureSettingsPermission() { // Write secure settings is a more protected permission. If caller has it we are good. if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) From 5dd09a84224d27e8b1785f3ad59cc715cb24eb51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Wed, 5 Jul 2023 13:52:21 +0200 Subject: [PATCH 196/208] Visit Uris added by WearableExtender Bug: 283962802 Test: atest + manual (POC app now crashes on notify() as expected) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a6f44e911f2d7204cc28c710e54f97c96231abab) Merged-In: I0da18c631eb5e4844a48760c7aaedab715a0bfed Change-Id: I0da18c631eb5e4844a48760c7aaedab715a0bfed --- core/java/android/app/Notification.java | 17 +++++++++++++++- .../NotificationManagerServiceTest.java | 20 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d0392abd35f9..d1b78e837102 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1831,6 +1831,10 @@ public Action build() { } } + private void visitUris(@NonNull Consumer visitor) { + visitIconUri(visitor, getIcon()); + } + @Override public Action clone() { return new Action( @@ -2508,7 +2512,7 @@ public void visitUris(@NonNull Consumer visitor) { if (actions != null) { for (Action action : actions) { - visitIconUri(visitor, action.getIcon()); + action.visitUris(visitor); } } @@ -2579,6 +2583,11 @@ public void visitUris(@NonNull Consumer visitor) { if (mBubbleMetadata != null) { visitIconUri(visitor, mBubbleMetadata.getIcon()); } + + if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); + } } /** @@ -10291,6 +10300,12 @@ private void setFlag(int mask, boolean value) { mFlags &= ~mask; } } + + private void visitUris(@NonNull Consumer visitor) { + for (Action action : mActions) { + action.visitUris(visitor); + } + } } /** diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 31983b931108..18c945441504 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4420,6 +4420,26 @@ public void testVisitUris_messagingStyle() { verify(visitor, times(1)).accept(eq(personIcon3.getUri())); } + @Test + public void testVisitUris_wearableExtender() { + Icon actionIcon = Icon.createWithContentUri("content://media/action"); + Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction"); + PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); + Notification n = new Notification.Builder(mContext, "a") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build()) + .extend(new Notification.WearableExtender().addAction( + new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build())) + .build(); + + Consumer visitor = (Consumer) spy(Consumer.class); + n.visitUris(visitor); + + verify(visitor).accept(eq(actionIcon.getUri())); + verify(visitor).accept(eq(wearActionIcon.getUri())); + } + @Test public void testSetNotificationPolicy_preP_setOldFields() { ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); From 30acb432d72953a318f0ac56ad905983938ce7c8 Mon Sep 17 00:00:00 2001 From: Lifu Tang Date: Wed, 5 Jul 2023 13:03:00 -0700 Subject: [PATCH 197/208] Fix bypass BAL via `requestGeofence` Bug: 273729172 Test: manually (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:7f9be7c3c859dc82d37452570d9878b58f6437a9) Merged-In: Ia8094244f908b20d42711b6ea8f58f9b3345b563 Change-Id: Ia8094244f908b20d42711b6ea8f58f9b3345b563 --- services/core/java/com/android/server/PendingIntentUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/PendingIntentUtils.java b/services/core/java/com/android/server/PendingIntentUtils.java index 1600101b20f4..a72a4d254a2a 100644 --- a/services/core/java/com/android/server/PendingIntentUtils.java +++ b/services/core/java/com/android/server/PendingIntentUtils.java @@ -34,6 +34,7 @@ public class PendingIntentUtils { public static Bundle createDontSendToRestrictedAppsBundle(@Nullable Bundle bundle) { final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setDontSendToRestrictedApps(true); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); if (bundle == null) { return options.toBundle(); } From e1adb6d36190f68f77161208d9b354f35422a1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Fri, 11 Aug 2023 18:27:57 +0200 Subject: [PATCH 198/208] Visit Uris related to Notification style extras Even if the corresponding styles themselves were not applied to the Notification.Builder. Test: atest NotificationManagerServiceTest Bug: 287640400 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a7e0c6585fd155d5bd9354b8b15516f4788c33a7) Merged-In: I25acab19be7dd486aabede8c91dbad5a1a217abf Change-Id: I25acab19be7dd486aabede8c91dbad5a1a217abf --- core/java/android/app/Notification.java | 14 ++++---- .../NotificationManagerServiceTest.java | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d1b78e837102..6a59329d4d42 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2533,11 +2533,6 @@ public void visitUris(@NonNull Consumer visitor) { } } - final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON); - if (person != null && person.getIconUri() != null) { - visitor.accept(person.getIconUri()); - } - final RemoteInputHistoryItem[] history = getParcelableArrayFromBundle(extras, Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (history != null) { @@ -2548,9 +2543,14 @@ public void visitUris(@NonNull Consumer visitor) { } } } - } - if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) { + // Extras for MessagingStyle. We visit them even if not isStyle(MessagingStyle), since + // Notification Listeners might use directly (without the isStyle check). + final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON); + if (person != null && person.getIconUri() != null) { + visitor.accept(person.getIconUri()); + } + final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 18c945441504..025f8a5d368a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4420,6 +4420,40 @@ public void testVisitUris_messagingStyle() { verify(visitor, times(1)).accept(eq(personIcon3.getUri())); } + @Test + public void testVisitUris_styleExtrasWithoutStyle() { + Notification notification = new Notification.Builder(mContext, "a") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .build(); + + Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle( + personWithIcon("content://user")) + .addHistoricMessage(new Notification.MessagingStyle.Message("Heyhey!", + System.currentTimeMillis(), + personWithIcon("content://historicalMessenger"))) + .addMessage(new Notification.MessagingStyle.Message("Are you there", + System.currentTimeMillis(), + personWithIcon("content://messenger"))) + .setShortcutIcon( + Icon.createWithContentUri("content://conversationShortcut")); + messagingStyle.addExtras(notification.extras); // Instead of Builder.setStyle(style). + + Consumer visitor = (Consumer) spy(Consumer.class); + notification.visitUris(visitor); + + verify(visitor).accept(eq(Uri.parse("content://user"))); + verify(visitor).accept(eq(Uri.parse("content://historicalMessenger"))); + verify(visitor).accept(eq(Uri.parse("content://messenger"))); + verify(visitor).accept(eq(Uri.parse("content://conversationShortcut"))); + } + + private static Person personWithIcon(String iconUri) { + return new Person.Builder() + .setName("Mr " + iconUri) + .setIcon(Icon.createWithContentUri(iconUri)) + .build(); + } + @Test public void testVisitUris_wearableExtender() { Icon actionIcon = Icon.createWithContentUri("content://media/action"); From c3add36ab98e553f5e7611bd23d646fef5fad6e7 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Fri, 23 Sep 2022 21:06:53 +0000 Subject: [PATCH 199/208] RESTRICT AUTOMERGE: Drop invalid data. Drop invalid data when writing or reading from XML. PersistableBundle does lazy unparcelling, so checking the values during unparcelling would remove the benefit of the lazy unparcelling. Checking the validity when writing to or reading from XML seems like the best alternative. Bug: 246542285 Bug: 247513680 Test: install test app with invalid job config, start app to schedule job, then check logcat and jobscheduler persisted file (cherry picked from commit 666e8ac60a31e2cc52b335b41004263f28a8db06) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:62b37ab21ce27746a79a2071deee98c61b23c8d9) Merged-In: Ie817aa0993e9046cb313a750d2323cadc8c1ef15 Change-Id: Ie817aa0993e9046cb313a750d2323cadc8c1ef15 --- core/java/android/os/PersistableBundle.java | 42 +++++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index 7a837e167fb0..7c7e2137aa17 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; +import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.util.FastXmlSerializer; @@ -45,6 +46,8 @@ */ public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable, XmlUtils.WriteMapCallback { + private static final String TAG = "PersistableBundle"; + private static final String TAG_PERSISTABLEMAP = "pbundle_as_map"; public static final PersistableBundle EMPTY; @@ -107,7 +110,11 @@ public PersistableBundle(PersistableBundle b) { * @hide */ public PersistableBundle(Bundle b) { - this(b.getMap()); + this(b, true); + } + + private PersistableBundle(Bundle b, boolean throwException) { + this(b.getMap(), throwException); } /** @@ -116,7 +123,7 @@ public PersistableBundle(Bundle b) { * @param map a Map containing only those items that can be persisted. * @throws IllegalArgumentException if any element of #map cannot be persisted. */ - private PersistableBundle(ArrayMap map) { + private PersistableBundle(ArrayMap map, boolean throwException) { super(); mFlags = FLAG_DEFUSABLE; @@ -125,16 +132,23 @@ private PersistableBundle(ArrayMap map) { // Now verify each item throwing an exception if there is a violation. final int N = mMap.size(); - for (int i=0; i= 0; --i) { Object value = mMap.valueAt(i); if (value instanceof ArrayMap) { // Fix up any Maps by replacing them with PersistableBundles. - mMap.setValueAt(i, new PersistableBundle((ArrayMap) value)); + mMap.setValueAt(i, + new PersistableBundle((ArrayMap) value, throwException)); } else if (value instanceof Bundle) { - mMap.setValueAt(i, new PersistableBundle(((Bundle) value))); + mMap.setValueAt(i, new PersistableBundle((Bundle) value, throwException)); } else if (!isValidType(value)) { - throw new IllegalArgumentException("Bad value in PersistableBundle key=" - + mMap.keyAt(i) + " value=" + value); + final String errorMsg = "Bad value in PersistableBundle key=" + + mMap.keyAt(i) + " value=" + value; + if (throwException) { + throw new IllegalArgumentException(errorMsg); + } else { + Slog.wtfStack(TAG, errorMsg); + mMap.removeAt(i); + } } } } @@ -249,6 +263,15 @@ public void writeUnknownObject(Object v, String name, XmlSerializer out) /** @hide */ public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { unparcel(); + // Explicitly drop invalid types an attacker may have added before persisting. + for (int i = mMap.size() - 1; i >= 0; --i) { + final Object value = mMap.valueAt(i); + if (!isValidType(value)) { + Slog.e(TAG, "Dropping bad data before persisting: " + + mMap.keyAt(i) + "=" + value); + mMap.removeAt(i); + } + } XmlUtils.writeMapXml(mMap, out, this); } @@ -297,9 +320,12 @@ public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOExcept while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { if (event == XmlPullParser.START_TAG) { + // Don't throw an exception when restoring from XML since an attacker could try to + // input invalid data in the persisted file. return new PersistableBundle((ArrayMap) XmlUtils.readThisArrayMapXml(in, startTag, tagName, - new MyReadMapCallback())); + new MyReadMapCallback()), + /* throwException */ false); } } return EMPTY; From e8c67590da19a7568c436a1b564ca1ee4e4e177e Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 12 Jul 2023 21:38:36 +0000 Subject: [PATCH 200/208] Validate URI-based shortcut icon at creation time. Bug: 288113797 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3d41fb7620ffb9c81b23977c8367c323e4721e65) Merged-In: I392f8e923923bf40827a2b6207c4eaa262694fbc Change-Id: I392f8e923923bf40827a2b6207c4eaa262694fbc --- .../android/server/pm/ShortcutService.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 4e73bd2cec40..961e06126788 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -30,6 +30,7 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -1804,11 +1805,32 @@ private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean f } if (shortcut.getIcon() != null) { ShortcutInfo.validateIcon(shortcut.getIcon()); + validateIconURI(shortcut); } shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED); } + // Validates the calling process has permission to access shortcut icon's image uri + private void validateIconURI(@NonNull final ShortcutInfo si) { + final int callingUid = injectBinderCallingUid(); + final Icon icon = si.getIcon(); + if (icon == null) { + // There's no icon in this shortcut, nothing to validate here. + return; + } + int iconType = icon.getType(); + if (iconType != Icon.TYPE_URI && iconType != Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // The icon is not URI-based, nothing to validate. + return; + } + final Uri uri = icon.getUri(); + mUriGrantsManagerInternal.checkGrantUriPermission(callingUid, si.getPackage(), + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(callingUid))); + } + private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false); } From 284956710264640557b6b0b81c71338256856254 Mon Sep 17 00:00:00 2001 From: Marzia Favaro Date: Mon, 31 Jul 2023 15:10:34 +0000 Subject: [PATCH 201/208] Require permission to unlock keyguard Bug: 288896339 Test: Manual, verify that the app which can be found on the bug can no longer call keyguardGoingAway successfully Require permission to unlock keyguard Bug: 288896339 Test: Manual, verify that the app which can be found on the bug can no longer call keyguardGoingAway successfully (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bd2aa5d309c5bf8e73161975bd5aba7945b25e84) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ef1345d278bd2a8944c6362bf65cff7305ca6fc5) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ad8e7e3b1db22684988a179e23639567a4096ca6) Merged-In: I7ba7e56f954c8e6f1f734311f735215918975bc6 Change-Id: I7ba7e56f954c8e6f1f734311f735215918975bc6 --- .../java/com/android/server/wm/ActivityTaskManagerService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 4ec53a584e8c..77a12c1604ef 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -18,6 +18,7 @@ import static android.Manifest.permission.BIND_VOICE_INTERACTION; import static android.Manifest.permission.CHANGE_CONFIGURATION; +import static android.Manifest.permission.CONTROL_KEYGUARD; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; @@ -3963,6 +3964,7 @@ public void showLockTaskEscapeMessage(IBinder token) { @Override public void keyguardGoingAway(int flags) { + mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, "unlock keyguard"); enforceNotIsolatedCaller("keyguardGoingAway"); final long token = Binder.clearCallingIdentity(); try { From 18c24630cb9ec16738a0e890472d415b80e6b11a Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Thu, 8 Jun 2023 14:38:19 -0700 Subject: [PATCH 202/208] Restrict number of shortcuts can be added through addDynamicShortcuts This CL fixes the issue where, when an app have multiple main activities, the total number of shortcuts can grow indefinitely if they were published through addDynamicShortcuts. Bug: 281061287 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2d93aabdc4905b36ee684533904029cfc61533b7) Merged-In: Ib3eecefee34517b670c59dd5b8526fe9eb24f463 Change-Id: Ib3eecefee34517b670c59dd5b8526fe9eb24f463 --- services/core/java/com/android/server/pm/ShortcutPackage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index b2d78ec7d95d..4bc3cdb730a3 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -305,6 +305,7 @@ private void forceReplaceShortcutInner(@NonNull ShortcutInfo newShortcut) { // Extract Icon and update the icon res ID and the bitmap path. s.saveIconAndFixUpShortcutLocked(newShortcut); s.fixUpShortcutResourceNamesAndValues(newShortcut); + ensureShortcutCountBeforePush(); mShortcuts.put(newShortcut.getId(), newShortcut); } @@ -351,7 +352,6 @@ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut, @NonNull List changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); - ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); From 6961808032baee76353c6ea0fba52bb43d0bd496 Mon Sep 17 00:00:00 2001 From: Pawan Wagh Date: Tue, 13 Jun 2023 17:37:26 +0000 Subject: [PATCH 203/208] Use readUniqueFileDescriptor in incidentd service readFileDescriptor doesn't provide ownership of the fds. fdopen needs ownership of the fds. Fds read from parcel should be duped in this scenario and readUniqueFileDescriptor dups fds internally. Test: m incidentd_service_fuzzer && adb sync data && adb shell /data/fuzz/x86_64/incidentd_service_fuzzer/incidentd_service_fuzzer Test: atest incidentd_test Bug: 286931110 Bug: 283699145 (cherry picked from commit ba78ef276951269f7b024baebdf1b8fa40bedb23) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b4aaf180ee8f3e375c7ab411f03cf9c24c1d8055) Merged-In: Ibe03a17dee91ac5bf25d123d4fd9c0bdd3c7d80e Change-Id: Ibe03a17dee91ac5bf25d123d4fd9c0bdd3c7d80e --- cmds/incidentd/src/IncidentService.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 13bf197aa9dc..421301adbff0 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -500,9 +500,13 @@ status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* switch (code) { case SHELL_COMMAND_TRANSACTION: { - int in = data.readFileDescriptor(); - int out = data.readFileDescriptor(); - int err = data.readFileDescriptor(); + unique_fd in, out, err; + if (status_t status = data.readUniqueFileDescriptor(&in); status != OK) return status; + + if (status_t status = data.readUniqueFileDescriptor(&out); status != OK) return status; + + if (status_t status = data.readUniqueFileDescriptor(&err); status != OK) return status; + int argc = data.readInt32(); Vector args; for (int i = 0; i < argc && data.dataAvail() > 0; i++) { @@ -512,15 +516,15 @@ status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* sp resultReceiver = IResultReceiver::asInterface(data.readStrongBinder()); - FILE* fin = fdopen(in, "r"); - FILE* fout = fdopen(out, "w"); - FILE* ferr = fdopen(err, "w"); + FILE* fin = fdopen(in.release(), "r"); + FILE* fout = fdopen(out.release(), "w"); + FILE* ferr = fdopen(err.release(), "w"); if (fin == NULL || fout == NULL || ferr == NULL) { resultReceiver->send(NO_MEMORY); } else { - err = command(fin, fout, ferr, args); - resultReceiver->send(err); + status_t result = command(fin, fout, ferr, args); + resultReceiver->send(result); } if (fin != NULL) { From 19fd931cb2d3dc8d38e349be9c92cd6a1edad3a0 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Mon, 24 Jul 2023 14:58:56 -0700 Subject: [PATCH 204/208] Validate userId when publishing shortcuts Bug: 288110451 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:01bfd04ff445db6290ae430d44ea1bf1a115fe3c) Merged-In: Idbde676f871db83825155730e3714f3727e25762 Change-Id: Idbde676f871db83825155730e3714f3727e25762 --- services/core/java/com/android/server/pm/ShortcutService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 961e06126788..2b00ab5d6669 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1642,6 +1642,10 @@ private void verifyShortcutInfoPackage(String callerPackage, ShortcutInfo si) { android.util.EventLog.writeEvent(0x534e4554, "109824443", -1, ""); throw new SecurityException("Shortcut package name mismatch"); } + final int callingUid = injectBinderCallingUid(); + if (UserHandle.getUserId(callingUid) != si.getUserId()) { + throw new SecurityException("User-ID in shortcut doesn't match the caller"); + } } private void verifyShortcutInfoPackages( From ebc737df459f66b91b2fef9d41adad5b4e4abbd2 Mon Sep 17 00:00:00 2001 From: Beverly Tai Date: Thu, 14 Sep 2023 20:50:28 +0000 Subject: [PATCH 205/208] Revert "On device lockdown, always show the keyguard" This reverts commit b23c2d5fb6630ea0da503b937f62880594b13e94. Reason for revert: b/300463732 regression Bug: 300463732 Bug: 218495634 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f57217125f2b124c16c463ef4507fb054cc1ba4f) Merged-In: I31485d0d8caa3060e998636b071dbe03f6b4fc82 Change-Id: I31485d0d8caa3060e998636b071dbe03f6b4fc82 --- .../systemui/keyguard/KeyguardViewMediator.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index d0229baa4108..5641459ce150 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -584,13 +584,6 @@ public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { notifyHasLockscreenWallpaperChanged(hasLockscreenWallpaper); } } - - @Override - public void onStrongAuthStateChanged(int userId) { - if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { - doKeyguardLocked(null); - } - } }; ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() { @@ -1407,8 +1400,7 @@ private void doKeyguardLocked(Bundle options) { } // if another app is disabling us, don't show - if (!mExternallyEnabled - && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + if (!mExternallyEnabled) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); mNeedToReshowWhenReenabled = true; From ad9eeee3ffa5e4863a5d8bc7b6b8f2d260bfb1ec Mon Sep 17 00:00:00 2001 From: Kunal Malhotra Date: Thu, 2 Feb 2023 23:48:27 +0000 Subject: [PATCH 206/208] Adding in verification of calling UID in onShellCommand Test: manual testing on device Bug: b/261709193 (cherry picked from commit b651d295b44eb82d664861b77f33dbde1bce9453) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3ef3f18ba3094c4cc4f954ba23d1da421f9ca8b0) Merged-In: I68903ebd6d3d85f4bc820b745e3233a448b62273 Change-Id: I68903ebd6d3d85f4bc820b745e3233a448b62273 --- .../java/com/android/server/am/ActivityManagerService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bd2e9e982106..34074bb2ab90 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10617,6 +10617,13 @@ public int getMemoryTrimLevel() { public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != ROOT_UID && callingUid != Process.SHELL_UID) { + if (resultReceiver != null) { + resultReceiver.send(-1, null); + } + throw new SecurityException("Shell commands are only callable by root or shell"); + } (new ActivityManagerShellCommand(this, false)).exec( this, in, out, err, args, callback, resultReceiver); } From 8009b050a162cc87ae2fac209e31beda72dbee82 Mon Sep 17 00:00:00 2001 From: Beverly Tai Date: Tue, 19 Sep 2023 21:01:11 +0000 Subject: [PATCH 207/208] Updated: always show the keyguard on device lockdown Additionally, don't hide keyguard when it's disabled if the user has locked down the device. Manual test steps: 1. Enable app pinning and disable "Ask for PIN before unpinning" setting 2. Pin an app (ie: Settings) 3. Lockdown from the power menu 4. Observe: user is brought to the keyguard, primary auth is required to enter the device. => After entering correct credential, the device is still in app pinning mode. => After entering an incorrect credential, the keyguard remains showing and the user can attempt again up to the limit Bug: 300463732 Bug: 218495634 Test: atest KeyguardViewMediatorTest Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:35a6e2f2c952440b1102033b2c3e496438503cff) Merged-In: I70fdae80f717712b3dfc9df54b9649959b4bb8f0 Change-Id: I70fdae80f717712b3dfc9df54b9649959b4bb8f0 --- .../systemui/keyguard/KeyguardViewMediator.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 5641459ce150..e544e0720896 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -569,6 +569,13 @@ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSo } } + @Override + public void onStrongAuthStateChanged(int userId) { + if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + doKeyguardLocked(null); + } + } + @Override public void onTrustChanged(int userId) { if (userId == KeyguardUpdateMonitor.getCurrentUser()) { @@ -1163,6 +1170,10 @@ public void setKeyguardEnabled(boolean enabled) { mExternallyEnabled = enabled; if (!enabled && mShowing) { + if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown"); + return; + } if (mExitSecureCallback != null) { if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring"); // we're in the process of handling a request to verify the user @@ -1399,8 +1410,9 @@ private void doKeyguardLocked(Bundle options) { return; } - // if another app is disabling us, don't show - if (!mExternallyEnabled) { + // if another app is disabling us, don't show unless we're in lockdown mode + if (!mExternallyEnabled + && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); mNeedToReshowWhenReenabled = true; From 9279d75d627abd35e464be2b635f2a3d1188c292 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Tue, 8 Aug 2023 16:19:48 -0500 Subject: [PATCH 208/208] RESTRICT AUTOMERGE: Check URI permissions for resumable media artwork When resumable media is added that has artwork set via URI, check the permissions for the URI before attempting to load it Test: atest MediaDataManagerTest UriGrantsManagerServiceTest Test: manual with test app Bug: 284297452 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c13567e39cd906c8544011de537490b70ce29b2f) Merged-In: Ie79915d3d1712f08dc2e8dfbd5bc7fd32bb308a3 Change-Id: Ie79915d3d1712f08dc2e8dfbd5bc7fd32bb308a3 --- core/java/android/app/IUriGrantsManager.aidl | 3 + .../systemui/media/MediaDataManager.kt | 35 +++++- .../systemui/media/MediaDataManagerTest.kt | 116 ++++++++++++++++++ .../server/uri/UriGrantsManagerService.java | 42 +++++++ 4 files changed, 195 insertions(+), 1 deletion(-) diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl index 9e7f2fecfea0..b630d034dca9 100644 --- a/core/java/android/app/IUriGrantsManager.aidl +++ b/core/java/android/app/IUriGrantsManager.aidl @@ -39,4 +39,7 @@ interface IUriGrantsManager { void clearGrantedUriPermissions(in String packageName, int userId); ParceledListSlice getUriPermissions(in String packageName, boolean incoming, boolean persistedOnly); + + int checkGrantUriPermission_ignoreNonSystem( + int sourceUid, String targetPkg, in Uri uri, int modeFlags, int userId); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index a758449f8c88..7524fc9feb26 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -18,11 +18,14 @@ package com.android.systemui.media import android.app.Notification import android.app.PendingIntent +import android.app.UriGrantsManager import android.content.BroadcastReceiver +import android.content.ContentProvider import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color @@ -34,6 +37,7 @@ import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaSession import android.net.Uri +import android.os.Process import android.os.UserHandle import android.service.notification.StatusBarNotification import android.text.TextUtils @@ -374,7 +378,13 @@ class MediaDataManager( // Album art var artworkBitmap = desc.iconBitmap if (artworkBitmap == null && desc.iconUri != null) { - artworkBitmap = loadBitmapFromUri(desc.iconUri!!) + val appUid = try { + context.packageManager.getApplicationInfo(packageName, 0)?.uid!! + } catch (e: PackageManager.NameNotFoundException) { + Log.w(TAG, "Could not get app UID for $packageName", e) + Process.INVALID_UID + } + artworkBitmap = loadBitmapFromUriForUser(desc.iconUri!!, userId, appUid, packageName) } val artworkIcon = if (artworkBitmap != null) { Icon.createWithBitmap(artworkBitmap) @@ -531,6 +541,29 @@ class MediaDataManager( return null } + /** Returns a bitmap if the user can access the given URI, else null */ + private fun loadBitmapFromUriForUser( + uri: Uri, + userId: Int, + appUid: Int, + packageName: String + ): Bitmap? { + try { + val ugm = UriGrantsManager.getService() + ugm.checkGrantUriPermission_ignoreNonSystem( + appUid, + packageName, + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, userId) + ) + return loadBitmapFromUri(uri) + } catch (e: SecurityException) { + Log.e(TAG, "Failed to get URI permission: $e") + } + return null + } + /** * Load a bitmap from a URI * @param uri the uri to load diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 780e64af3b4d..e7a1ad21d363 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -1,16 +1,21 @@ package com.android.systemui.media +import android.app.IUriGrantsManager import android.app.Notification.MediaStyle import android.app.PendingIntent +import android.app.UriGrantsManager import android.graphics.Bitmap +import android.graphics.ImageDecoder import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaSession +import android.net.Uri import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher @@ -27,6 +32,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito @@ -36,6 +42,7 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +import org.mockito.quality.Strictness private const val KEY = "KEY" private const val KEY_2 = "KEY_2" @@ -76,6 +83,8 @@ class MediaDataManagerTest : SysuiTestCase() { lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification @Captor lateinit var mediaDataCaptor: ArgumentCaptor + @Mock private lateinit var ugm: IUriGrantsManager + @Mock private lateinit var imageSource: ImageDecoder.Source @Before fun setup() { @@ -361,4 +370,111 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) assertThat(mediaDataCaptor.value!!.backgroundColor).isEqualTo(DEFAULT_COLOR) } + + @Test + fun testResumeMediaLoaded_hasArtPermission_artLoaded() { + // When resume media is loaded and user/app has permission to access the art URI, + var mockSession = ExtendedMockito.mockitoSession() + .mockStatic(UriGrantsManager::class.java) + .mockStatic(ImageDecoder::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + try { + whenever(UriGrantsManager.getService()).thenReturn(ugm) + whenever( + ugm.checkGrantUriPermission_ignoreNonSystem( + anyInt(), + anyObject(), + anyObject(), + anyInt(), + anyInt() + ) + ) + .thenReturn(1) + val artwork = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val uri = Uri.parse("content://example") + whenever(ImageDecoder.createSource(anyObject(), eq(uri))).thenReturn(imageSource) + whenever(ImageDecoder.decodeBitmap(anyObject(), anyObject())).thenReturn(artwork) + + val desc = + MediaDescription.Builder().run { + setTitle(SESSION_TITLE) + setIconUri(uri) + build() + } + addResumeControlAndLoad(desc) + + // Then the artwork is loaded + assertThat(mediaDataCaptor.value.artwork).isNotNull() + } finally { + mockSession.finishMocking() + } + } + + @Test + fun testResumeMediaLoaded_noArtPermission_noArtLoaded() { + // When resume media is loaded and user/app does not have permission to access the art URI + var mockSession = ExtendedMockito.mockitoSession() + .mockStatic(UriGrantsManager::class.java) + .mockStatic(ImageDecoder::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + try { + whenever(UriGrantsManager.getService()).thenReturn(ugm) + whenever( + ugm.checkGrantUriPermission_ignoreNonSystem( + anyInt(), + anyObject(), + anyObject(), + anyInt(), + anyInt() + ) + ) + .thenThrow(SecurityException("Test no permission")) + val artwork = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val uri = Uri.parse("content://example") + whenever(ImageDecoder.createSource(anyObject(), eq(uri))).thenReturn(imageSource) + whenever(ImageDecoder.decodeBitmap(anyObject(), anyObject())).thenReturn(artwork) + + val desc = + MediaDescription.Builder().run { + setTitle(SESSION_TITLE) + setIconUri(uri) + build() + } + addResumeControlAndLoad(desc) + + // Then the artwork is not loaded + assertThat(mediaDataCaptor.value.artwork).isNull() + } finally { + mockSession.finishMocking() + } + } + + /** Helper function to add a resumption control and capture the resulting MediaData */ + private fun addResumeControlAndLoad( + desc: MediaDescription, + packageName: String = PACKAGE_NAME + ) { + val listener = mock(MediaDataManager.Listener::class.java) + mediaDataManager.addListener(listener) + mediaDataManager.addResumptionControls( + USER_ID, + desc, + Runnable {}, + session.sessionToken, + APP_NAME, + pendingIntent, + packageName + ) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + + verify(listener) + .onMediaDataLoaded( + eq(packageName), + eq(null), + capture(mediaDataCaptor) + ) + } } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index e8a0379473da..0871497e4143 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -45,6 +45,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; @@ -70,6 +71,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -1331,6 +1333,46 @@ private boolean checkUriPermissionLocked(GrantUri grantUri, int uid, final int m return false; } + /** + * Check if the targetPkg can be granted permission to access uri by + * the callingUid using the given modeFlags. See {@link #checkGrantUriPermissionUnlocked}. + * + * @param callingUid The uid of the grantor app that has permissions to the uri. + * @param targetPkg The package name of the granted app that needs permissions to the uri. + * @param uri The uri for which permissions should be granted. + * @param modeFlags The modes to grant. See {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc. + * @param userId The userId in which the uri is to be resolved. + * @return uid of the target or -1 if permission grant not required. Returns -1 if the caller + * does not hold INTERACT_ACROSS_USERS_FULL + * @throws SecurityException if the grant is not allowed. + */ + @Override + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + public int checkGrantUriPermission_ignoreNonSystem(int callingUid, String targetPkg, Uri uri, + int modeFlags, int userId) { + if (!isCallerIsSystemOrPrivileged()) { + return Process.INVALID_UID; + } + final long origId = Binder.clearCallingIdentity(); + try { + return checkGrantUriPermissionUnlocked(callingUid, targetPkg, uri, modeFlags, + userId); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + private boolean isCallerIsSystemOrPrivileged() { + final int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) { + return true; + } + return ActivityManager.checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + uid, /* owningUid = */-1, /* exported = */ true) + == PackageManager.PERMISSION_GRANTED; + } + @GuardedBy("mLock") private void writeGrantedUriPermissionsLocked() { if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");