summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-05-11 18:42:05 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-05-11 18:42:05 +0000
commita267ee1bc216c7e62ec984a88aa2310762bb5519 (patch)
treeabbfffbd6ed474912186f60a8b965eb7344987d0
parentc39d2cf4662903fc19f6550ec2fd468d25a19adb (diff)
parent91bfcbbd87886049778142618a655352b16cd911 (diff)
downloadbase-a267ee1bc216c7e62ec984a88aa2310762bb5519.tar.gz
Merge cherrypicks of ['googleplex-android-review.googlesource.com/20219552', 'googleplex-android-review.googlesource.com/20946190', 'googleplex-android-review.googlesource.com/20460002', 'googleplex-android-review.googlesource.com/22390145', 'googleplex-android-review.googlesource.com/22616881', 'googleplex-android-review.googlesource.com/21327525', 'googleplex-android-review.googlesource.com/22621774', 'googleplex-android-review.googlesource.com/22898060', 'googleplex-android-review.googlesource.com/17738545', 'googleplex-android-review.googlesource.com/21253698', 'googleplex-android-review.googlesource.com/22787457', 'googleplex-android-review.googlesource.com/22509574', 'googleplex-android-review.googlesource.com/22895638'] into security-aosp-tm-release.android-security-13.0.0_r7
Change-Id: Ied11657b48451b612e638edbf54755756e2fd348
-rw-r--r--core/java/android/app/ActivityOptions.java6
-rw-r--r--core/java/android/app/Notification.java19
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java20
-rw-r--r--core/java/android/permission/IPermissionManager.aidl3
-rw-r--r--core/java/android/permission/PermissionManager.java3
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java10
-rw-r--r--core/java/android/util/jar/StrictJarVerifier.java11
-rw-r--r--core/java/android/widget/RemoteViews.java6
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsTest.java64
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java2
-rw-r--r--packages/VpnDialogs/res/values/strings.xml28
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java53
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java20
-rw-r--r--services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java230
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java9
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java55
17 files changed, 406 insertions, 139 deletions
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 0ba1614ab6c5..7ed3baa48c1f 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -20,6 +20,8 @@ import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIO
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
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.IntDef;
@@ -1684,7 +1686,9 @@ public class ActivityOptions extends ComponentOptions {
* @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);
}
/**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e82073380394..b3921fe8bd79 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2853,6 +2853,17 @@ public class Notification implements Parcelable
if (person != 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 (isStyle(MessagingStyle.class) && extras != null) {
@@ -2883,6 +2894,14 @@ public class Notification implements Parcelable
}
}
+ if (isStyle(CallStyle.class) & extras != null) {
+ Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON);
+ if (callPerson != null) {
+ visitor.accept(callPerson.getIconUri());
+ }
+ visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON));
+ }
+
if (mBubbleMetadata != null) {
visitIconUri(visitor, mBubbleMetadata.getIcon());
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 52774e354c90..c33390398400 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -283,6 +283,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,
@@ -475,8 +481,7 @@ public final class ShortcutInfo implements Parcelable {
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.
@@ -582,6 +587,14 @@ public final class ShortcutInfo implements Parcelable {
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.
*
@@ -2336,7 +2349,8 @@ public final class ShortcutInfo implements Parcelable {
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, android.content.ComponentName.class);
mFlags = source.readInt();
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 6a93b354f4da..45dad9861406 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -77,8 +77,7 @@ interface IPermissionManager {
List<SplitPermissionInfoParcelable> getSplitPermissions();
void startOneTimePermissionSession(String packageName, int userId, long timeout,
- long revokeAfterKilledDelay, int importanceToResetTimer,
- int importanceToKeepSessionAlive);
+ long revokeAfterKilledDelay);
void stopOneTimePermissionSession(String packageName, int userId);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 6b540d72bba0..67699543131a 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1371,8 +1371,7 @@ public final class PermissionManager {
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
try {
mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
- timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
- importanceToKeepSessionAlive);
+ timeoutMillis, revokeAfterKilledDelayMillis);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index c8c1fd4eba21..9801559854f9 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.
*
* <p><b>NOTE: This method does not verify the signature.</b>
@@ -182,6 +187,11 @@ public class ApkSignatureSchemeV2Verifier {
}
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<String, byte[]> metaEntries;
@@ -293,10 +298,16 @@ class StrictJarVerifier {
return false;
}
+ int signerCount = 0;
Iterator<String> 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();
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2879cd888d2d..4a920e3b71db 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -728,6 +728,12 @@ public class RemoteViews implements Parcelable, Filter {
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<Uri> visitor) {
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 00b3693c902b..a9d3ce51b2c3 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -24,6 +24,10 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
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;
@@ -33,6 +37,8 @@ import android.content.Intent;
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.Looper;
@@ -58,6 +64,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
/**
* Tests for RemoteViews.
@@ -690,4 +697,61 @@ public class RemoteViewsTest {
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<Uri> visitor = (Consumer<Uri>) 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<Uri> visitor = (Consumer<Uri>) 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()));
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 2b9553d3eda2..0309d212e03f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -469,7 +469,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
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;
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index f971a0916837..a85b8e4ff553 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -100,4 +100,32 @@
without any consequences. [CHAR LIMIT=20] -->
<string name="dismiss">Dismiss</string>
+ <!-- Malicious VPN apps may provide very long labels or cunning HTML to trick the system dialogs
+ into displaying what they want. The system will attempt to sanitize the label, and if the
+ label is deemed dangerous, then this string is used instead. The first argument is the
+ first 30 characters of the label, and the second argument is the package name of the app.
+ Example : Normally a VPN app may be called "My VPN app" in which case the dialog will read
+ "My VPN app wants to set up a VPN connection...". If the label is very long, then, this
+ will be used to show "VerylongVPNlabel… (com.my.vpn.app) wants to set up a VPN
+ connection...". For this case, the code will refer to sanitized_vpn_label_with_ellipsis.
+ -->
+ <string name="sanitized_vpn_label_with_ellipsis">
+ <xliff:g id="sanitized_vpn_label_with_ellipsis" example="My VPN app">%1$s</xliff:g>… (
+ <xliff:g id="sanitized_vpn_label_with_ellipsis" example="com.my.vpn.app">%2$s</xliff:g>)
+ </string>
+
+ <!-- Malicious VPN apps may provide very long labels or cunning HTML to trick the system dialogs
+ into displaying what they want. The system will attempt to sanitize the label, and if the
+ label is deemed dangerous, then this string is used instead. The first argument is the
+ label, and the second argument is the package name of the app.
+ Example : Normally a VPN app may be called "My VPN app" in which case the dialog will read
+ "My VPN app wants to set up a VPN connection...". If the VPN label contains HTML tag but
+ the length is not very long, the dialog will show "VpnLabelWith&lt;br&gt;HtmlTag
+ (com.my.vpn.app) wants to set up a VPN connection...". For this case, the code will refer
+ to sanitized_vpn_label.
+ -->
+ <string name="sanitized_vpn_label">
+ <xliff:g id="sanitized_vpn_label" example="My VPN app">%1$s</xliff:g> (
+ <xliff:g id="sanitized_vpn_label" example="com.my.vpn.app">%2$s</xliff:g>)
+ </string>
</resources>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index fb2367843fc1..2b3202e0a982 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -40,12 +40,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 VpnManager mVm;
+ private View mView;
+
public ConfirmDialog() {
this(VpnManager.TYPE_VPN_SERVICE);
}
@@ -54,6 +60,42 @@ public class ConfirmDialog extends AlertActivity
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&lt;br&gt;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);
@@ -75,15 +117,16 @@ public class ConfirmDialog extends AlertActivity
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);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 5d73de103e90..1a7d8e968edf 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2093,9 +2093,25 @@ final class InstallPackageHelper {
// The caller explicitly specified INSTALL_ALL_USERS flag.
// Thus, updating the settings to install the app for all users.
for (int currentUserId : allUsers) {
- ps.setInstalled(true, currentUserId);
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId,
+ // If the app is already installed for the currentUser,
+ // keep it as installed as we might be updating the app at this place.
+ // If not currently installed, check if the currentUser is restricted by
+ // DISALLOW_INSTALL_APPS or DISALLOW_DEBUGGING_FEATURES device policy.
+ // Install / update the app if the user isn't restricted. Skip otherwise.
+ final boolean installedForCurrentUser = ArrayUtils.contains(
+ installedForUsers, currentUserId);
+ final boolean restrictedByPolicy =
+ mPm.isUserRestricted(currentUserId,
+ UserManager.DISALLOW_INSTALL_APPS)
+ || mPm.isUserRestricted(currentUserId,
+ UserManager.DISALLOW_DEBUGGING_FEATURES);
+ if (installedForCurrentUser || !restrictedByPolicy) {
+ ps.setInstalled(true, currentUserId);
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
installerPackageName);
+ } else {
+ ps.setInstalled(false, currentUserId);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 881f8707fdd8..d28048ce74c7 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -16,23 +16,26 @@
package com.android.server.pm.permission;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
-
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.RemoteException;
import android.permission.PermissionControllerManager;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
/**
* Class that handles one-time permissions for a user
@@ -47,7 +50,8 @@ public class OneTimePermissionUserManager {
"one_time_permissions_killed_delay_millis";
private final @NonNull Context mContext;
- private final @NonNull ActivityManager mActivityManager;
+ private final @NonNull IActivityManager mIActivityManager;
+ private final @NonNull ActivityManagerInternal mActivityManagerInternal;
private final @NonNull AlarmManager mAlarmManager;
private final @NonNull PermissionControllerManager mPermissionControllerManager;
@@ -77,49 +81,15 @@ public class OneTimePermissionUserManager {
OneTimePermissionUserManager(@NonNull Context context) {
mContext = context;
- mActivityManager = context.getSystemService(ActivityManager.class);
+ mIActivityManager = ActivityManager.getService();
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAlarmManager = context.getSystemService(AlarmManager.class);
mPermissionControllerManager = context.getSystemService(PermissionControllerManager.class);
mHandler = context.getMainThreadHandler();
}
- /**
- * Starts a one-time permission session for a given package. A one-time permission session is
- * ended if app becomes inactive. Inactivity is defined as the package's uid importance level
- * staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid
- * importance level goes <= importanceToResetTimer then the timer is reset and doesn't start
- * until going > importanceToResetTimer.
- * <p>
- * When this timeoutMillis is reached if the importance level is <= importanceToKeepSessionAlive
- * then the session is extended until either the importance goes above
- * importanceToKeepSessionAlive which will end the session or <= importanceToResetTimer which
- * will continue the session and reset the timer.
- * </p>
- * <p>
- * Importance levels are defined in {@link android.app.ActivityManager.RunningAppProcessInfo}.
- * </p>
- * <p>
- * Once the session ends PermissionControllerService#onNotifyOneTimePermissionSessionTimeout
- * is invoked.
- * </p>
- * <p>
- * Note that if there is currently an active session for a package a new one isn't created and
- * the existing one isn't changed.
- * </p>
- * @param packageName The package to start a one-time permission session for
- * @param timeoutMillis Number of milliseconds for an app to be in an inactive state
- * @param revokeAfterKilledDelayMillis Number of milliseconds to wait after the process dies
- * before ending the session. Set to -1 to use default value
- * for the device.
- * @param importanceToResetTimer The least important level to uid must be to reset the timer
- * @param importanceToKeepSessionAlive The least important level the uid must be to keep the
- * session alive
- *
- * @hide
- */
void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
- long revokeAfterKilledDelayMillis, int importanceToResetTimer,
- int importanceToKeepSessionAlive) {
+ long revokeAfterKilledDelayMillis) {
int uid;
try {
uid = mContext.getPackageManager().getPackageUid(packageName, 0);
@@ -131,13 +101,11 @@ public class OneTimePermissionUserManager {
synchronized (mLock) {
PackageInactivityListener listener = mListeners.get(uid);
if (listener != null) {
- listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis,
- importanceToResetTimer, importanceToKeepSessionAlive);
+ listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis);
return;
}
listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
- revokeAfterKilledDelayMillis, importanceToResetTimer,
- importanceToKeepSessionAlive);
+ revokeAfterKilledDelayMillis);
mListeners.put(uid, listener);
}
}
@@ -182,34 +150,58 @@ public class OneTimePermissionUserManager {
private static final long TIMER_INACTIVE = -1;
+ private static final int STATE_GONE = 0;
+ private static final int STATE_TIMER = 1;
+ private static final int STATE_ACTIVE = 2;
+
private final int mUid;
private final @NonNull String mPackageName;
private long mTimeout;
private long mRevokeAfterKilledDelay;
- private int mImportanceToResetTimer;
- private int mImportanceToKeepSessionAlive;
private boolean mIsAlarmSet;
private boolean mIsFinished;
private long mTimerStart = TIMER_INACTIVE;
- private final ActivityManager.OnUidImportanceListener mStartTimerListener;
- private final ActivityManager.OnUidImportanceListener mSessionKillableListener;
- private final ActivityManager.OnUidImportanceListener mGoneListener;
-
private final Object mInnerLock = new Object();
private final Object mToken = new Object();
+ private final IUidObserver.Stub mObserver = new IUidObserver.Stub() {
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ if (uid == mUid) {
+ PackageInactivityListener.this.updateUidState(STATE_GONE);
+ }
+ }
- private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
- long revokeAfterkilledDelay, int importanceToResetTimer,
- int importanceToKeepSessionAlive) {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq,
+ int capability) {
+ if (uid == mUid) {
+ if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ && procState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ PackageInactivityListener.this.updateUidState(STATE_TIMER);
+ } else {
+ PackageInactivityListener.this.updateUidState(STATE_ACTIVE);
+ }
+ }
+ }
+
+ public void onUidActive(int uid) {
+ }
+ public void onUidIdle(int uid, boolean disabled) {
+ }
+ public void onUidProcAdjChanged(int uid) {
+ }
+ public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+ private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
+ long revokeAfterkilledDelay) {
Log.i(LOG_TAG,
"Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
- + " killedDelay=" + revokeAfterkilledDelay
- + " importanceToResetTimer=" + importanceToResetTimer
- + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
+ + " killedDelay=" + revokeAfterkilledDelay);
mUid = uid;
mPackageName = packageName;
@@ -219,27 +211,24 @@ public class OneTimePermissionUserManager {
DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_KILLED_DELAY_CONFIG_KEY,
DEFAULT_KILLED_DELAY_MILLIS)
: revokeAfterkilledDelay;
- mImportanceToResetTimer = importanceToResetTimer;
- mImportanceToKeepSessionAlive = importanceToKeepSessionAlive;
-
- mStartTimerListener =
- (changingUid, importance) -> onImportanceChanged(changingUid, importance);
- mSessionKillableListener =
- (changingUid, importance) -> onImportanceChanged(changingUid, importance);
- mGoneListener =
- (changingUid, importance) -> onImportanceChanged(changingUid, importance);
-
- mActivityManager.addOnUidImportanceListener(mStartTimerListener,
- importanceToResetTimer);
- mActivityManager.addOnUidImportanceListener(mSessionKillableListener,
- importanceToKeepSessionAlive);
- mActivityManager.addOnUidImportanceListener(mGoneListener, IMPORTANCE_CACHED);
-
- onImportanceChanged(mUid, mActivityManager.getPackageImportance(packageName));
+
+ try {
+ mIActivityManager.registerUidObserver(mObserver,
+ ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ null);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Couldn't check uid proc state", e);
+ // Can't register uid observer, just revoke immediately
+ synchronized (mInnerLock) {
+ onPackageInactiveLocked();
+ }
+ }
+
+ updateUidState();
}
- public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis,
- int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis) {
synchronized (mInnerLock) {
mTimeout = Math.min(mTimeout, timeoutMillis);
mRevokeAfterKilledDelay = Math.min(mRevokeAfterKilledDelay,
@@ -248,63 +237,74 @@ public class OneTimePermissionUserManager {
DeviceConfig.NAMESPACE_PERMISSIONS,
PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
: revokeAfterKilledDelayMillis);
- mImportanceToResetTimer = Math.min(importanceToResetTimer, mImportanceToResetTimer);
- mImportanceToKeepSessionAlive = Math.min(importanceToKeepSessionAlive,
- mImportanceToKeepSessionAlive);
Log.v(LOG_TAG,
"Updated params for " + mPackageName + ". timeout=" + mTimeout
- + " killedDelay=" + mRevokeAfterKilledDelay
- + " importanceToResetTimer=" + mImportanceToResetTimer
- + " importanceToKeepSessionAlive=" + mImportanceToKeepSessionAlive);
- onImportanceChanged(mUid, mActivityManager.getPackageImportance(mPackageName));
+ + " killedDelay=" + mRevokeAfterKilledDelay);
+ updateUidState();
}
}
- private void onImportanceChanged(int uid, int importance) {
- if (uid != mUid) {
- return;
+ private int getCurrentState() {
+ return getStateFromProcState(mActivityManagerInternal.getUidProcessState(mUid));
+ }
+
+ private int getStateFromProcState(int procState) {
+ if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ return STATE_GONE;
+ } else {
+ if (procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ return STATE_TIMER;
+ } else {
+ return STATE_ACTIVE;
+ }
}
+ }
- Log.v(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
- + " importance=" + importance);
+ private void updateUidState() {
+ updateUidState(getCurrentState());
+ }
+
+ private void updateUidState(int state) {
+ Log.v(LOG_TAG, "Updating state for " + mPackageName + " (" + mUid + ")."
+ + " state=" + state);
synchronized (mInnerLock) {
// Remove any pending inactivity callback
mHandler.removeCallbacksAndMessages(mToken);
- if (importance > IMPORTANCE_CACHED) {
+ if (state == STATE_GONE) {
if (mRevokeAfterKilledDelay == 0) {
onPackageInactiveLocked();
return;
}
// Delay revocation in case app is restarting
mHandler.postDelayed(() -> {
- int imp = mActivityManager.getUidImportance(mUid);
- if (imp > IMPORTANCE_CACHED) {
- onPackageInactiveLocked();
- } else {
- if (DEBUG) {
- Log.d(LOG_TAG, "No longer gone after delayed revocation. "
- + "Rechecking for " + mPackageName + " (" + mUid + ").");
+ int currentState;
+ synchronized (mInnerLock) {
+ currentState = getCurrentState();
+ if (currentState == STATE_GONE) {
+ onPackageInactiveLocked();
+ return;
}
- onImportanceChanged(mUid, imp);
}
+ if (DEBUG) {
+ Log.d(LOG_TAG, "No longer gone after delayed revocation. "
+ + "Rechecking for " + mPackageName + " (" + mUid
+ + ").");
+ }
+ updateUidState(currentState);
}, mToken, mRevokeAfterKilledDelay);
return;
- }
- if (importance > mImportanceToResetTimer) {
+ } else if (state == STATE_TIMER) {
if (mTimerStart == TIMER_INACTIVE) {
if (DEBUG) {
Log.d(LOG_TAG, "Start the timer for "
+ mPackageName + " (" + mUid + ").");
}
mTimerStart = System.currentTimeMillis();
+ setAlarmLocked();
}
- } else {
+ } else if (state == STATE_ACTIVE) {
mTimerStart = TIMER_INACTIVE;
- }
- if (importance > mImportanceToKeepSessionAlive) {
- setAlarmLocked();
- } else {
cancelAlarmLocked();
}
}
@@ -318,19 +318,9 @@ public class OneTimePermissionUserManager {
mIsFinished = true;
cancelAlarmLocked();
try {
- mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "Could not remove start timer listener", e);
- }
- try {
- mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "Could not remove session killable listener", e);
- }
- try {
- mActivityManager.removeOnUidImportanceListener(mGoneListener);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "Could not remove gone listener", e);
+ mIActivityManager.unregisterUidObserver(mObserver);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Unable to unregister uid observer.", e);
}
}
}
@@ -394,9 +384,11 @@ public class OneTimePermissionUserManager {
mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
mPackageName);
});
- mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
- mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
- mActivityManager.removeOnUidImportanceListener(mGoneListener);
+ try {
+ mIActivityManager.unregisterUidObserver(mObserver);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Unable to unregister uid observer.", e);
+ }
synchronized (mLock) {
mListeners.remove(mUid);
}
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 2c81b21d37bc..1d6a74b2e917 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -386,8 +386,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
- long timeoutMillis, long revokeAfterKilledDelayMillis, int importanceToResetTimer,
- int importanceToKeepSessionAlive) {
+ long timeoutMillis, long revokeAfterKilledDelayMillis) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
"Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
@@ -397,8 +396,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
- timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
- importanceToKeepSessionAlive);
+ timeoutMillis, revokeAfterKilledDelayMillis);
} finally {
Binder.restoreCallingIdentity(token);
}
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 c78678431dac..86d4655e9d3a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -228,6 +228,15 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
});
}
+ public void testShortcutIdTruncated() {
+ ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(),
+ "s".repeat(Short.MAX_VALUE)).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)
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 c72bd4e91b8d..cc0bc24fc660 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -123,6 +123,7 @@ import android.app.NotificationManager;
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;
@@ -5164,10 +5165,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
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")
@@ -5179,6 +5206,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
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
+ public void testVisitUris_callStyle() {
+ Icon personIcon = Icon.createWithContentUri("content://media/person");
+ Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
+ Person callingPerson = new Person.Builder().setName("Someone")
+ .setIcon(personIcon)
+ .build();
+ PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
+ Notification n = new Notification.Builder(mContext, "a")
+ .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent)
+ .setVerificationIcon(verificationIcon))
+ .setContentTitle("Calling...")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .build();
+
+ Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+ n.visitUris(visitor);
+
+ verify(visitor, times(1)).accept(eq(personIcon.getUri()));
+ verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
}
@Test