summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java4
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java14
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java20
-rw-r--r--core/java/android/os/BaseBundle.java10
-rw-r--r--core/java/android/os/Parcel.java12
-rw-r--r--core/java/android/widget/RemoteViews.java14
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java50
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java27
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java2
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java545
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java39
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java111
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java15
-rw-r--r--services/core/java/com/android/server/notification/PermissionHelper.java21
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java32
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java4
-rw-r--r--services/core/java/com/android/server/wm/Transition.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java51
-rw-r--r--services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt5
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java37
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java25
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java133
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java5
-rw-r--r--telecomm/java/android/telecom/StatusHints.java34
41 files changed, 1197 insertions, 230 deletions
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 27f6a266597c..8926ff7a98c6 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
-import android.app.LoadedApk;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -733,9 +732,6 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
*/
protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
try {
- ApplicationInfo expectedAppInfo = mInfo.providerInfo.applicationInfo;
- LoadedApk.checkAndUpdateApkPaths(expectedAppInfo);
- // Return if cloned successfully, otherwise default
Context newContext = mContext.createApplicationContext(
mInfo.providerInfo.applicationInfo,
Context.CONTEXT_RESTRICTED);
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 2e40f6096ccb..71d1015cd6e3 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -460,6 +460,20 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
+ * Set the class name of ConfirmDeviceCredentialActivity.
+ *
+ * @return This builder.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
+ public Builder setClassNameIfItIsConfirmDeviceCredentialActivity() {
+ mPromptInfo.setClassNameIfItIsConfirmDeviceCredentialActivity(
+ mContext.getClass().getName());
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 02aad1dc4b4b..f1fce7290f99 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -48,6 +48,7 @@ public class PromptInfo implements Parcelable {
private boolean mAllowBackgroundAuthentication;
private boolean mIgnoreEnrollmentState;
private boolean mIsForLegacyFingerprintManager = false;
+ private String mClassNameIfItIsConfirmDeviceCredentialActivity = null;
public PromptInfo() {
@@ -72,6 +73,7 @@ public class PromptInfo implements Parcelable {
mAllowBackgroundAuthentication = in.readBoolean();
mIgnoreEnrollmentState = in.readBoolean();
mIsForLegacyFingerprintManager = in.readBoolean();
+ mClassNameIfItIsConfirmDeviceCredentialActivity = in.readString();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -111,6 +113,7 @@ public class PromptInfo implements Parcelable {
dest.writeBoolean(mAllowBackgroundAuthentication);
dest.writeBoolean(mIgnoreEnrollmentState);
dest.writeBoolean(mIsForLegacyFingerprintManager);
+ dest.writeString(mClassNameIfItIsConfirmDeviceCredentialActivity);
}
public boolean containsTestConfigurations() {
@@ -122,6 +125,8 @@ public class PromptInfo implements Parcelable {
return true;
} else if (mAllowBackgroundAuthentication) {
return true;
+ } else if (mClassNameIfItIsConfirmDeviceCredentialActivity != null) {
+ return true;
}
return false;
}
@@ -222,6 +227,13 @@ public class PromptInfo implements Parcelable {
mAllowedSensorIds.add(sensorId);
}
+ /**
+ * Set the class name of ConfirmDeviceCredentialActivity.
+ */
+ void setClassNameIfItIsConfirmDeviceCredentialActivity(String className) {
+ mClassNameIfItIsConfirmDeviceCredentialActivity = className;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -303,4 +315,12 @@ public class PromptInfo implements Parcelable {
public boolean isForLegacyFingerprintManager() {
return mIsForLegacyFingerprintManager;
}
+
+ /**
+ * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is
+ * not ConfirmDeviceCredentialActivity.
+ */
+ public String getClassNameIfItIsConfirmDeviceCredentialActivity() {
+ return mClassNameIfItIsConfirmDeviceCredentialActivity;
+ }
}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 4e3adfbdda3f..9773134f6419 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -471,10 +471,10 @@ public class BaseBundle {
map.erase();
map.ensureCapacity(count);
}
- int numLazyValues = 0;
+ int[] numLazyValues = new int[]{0};
try {
- numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ ownsParcel, mClassLoader);
+ parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ ownsParcel, mClassLoader, numLazyValues);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -485,14 +485,14 @@ public class BaseBundle {
} finally {
mWeakParcelledData = null;
if (ownsParcel) {
- if (numLazyValues == 0) {
+ if (numLazyValues[0] == 0) {
recycleParcel(parcelledData);
} else {
mWeakParcelledData = new WeakReference<>(parcelledData);
}
}
- mLazyValues = numLazyValues;
+ mLazyValues = numLazyValues[0];
mParcelledByNative = false;
mMap = map;
// Set field last as it is volatile
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 453aba34dbcf..11eb5ccb709c 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -5335,7 +5335,7 @@ public final class Parcel {
private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
int size, @Nullable ClassLoader loader) {
- readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
+ readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader, null);
}
/**
@@ -5345,17 +5345,16 @@ public final class Parcel {
* @param lazy Whether to populate the map with lazy {@link Function} objects for
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
* details.
- * @return a count of the lazy values in the map
+ * @param lazyValueCount number of lazy values added here
* @hide
*/
- int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
- boolean lazy, @Nullable ClassLoader loader) {
- int lazyValues = 0;
+ void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+ boolean lazy, @Nullable ClassLoader loader, int[] lazyValueCount) {
while (size > 0) {
String key = readString();
Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
if (value instanceof LazyValue) {
- lazyValues++;
+ lazyValueCount[0]++;
}
if (sorted) {
map.append(key, value);
@@ -5367,7 +5366,6 @@ public final class Parcel {
if (sorted) {
map.validate();
}
- return lazyValues;
}
/**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 88ce92f24702..7db77b12b3dc 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -34,7 +34,6 @@ import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
-import android.app.LoadedApk;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
@@ -6057,9 +6056,18 @@ public class RemoteViews implements Parcelable, Filter {
return context;
}
try {
- LoadedApk.checkAndUpdateApkPaths(mApplication);
- return context.createApplicationContext(mApplication,
+ // Use PackageManager as the source of truth for application information, rather
+ // than the parceled ApplicationInfo provided by the app.
+ ApplicationInfo sanitizedApplication =
+ context.getPackageManager().getApplicationInfoAsUser(
+ mApplication.packageName, 0,
+ UserHandle.getUserId(mApplication.uid));
+ Context applicationContext = context.createApplicationContext(
+ sanitizedApplication,
Context.CONTEXT_RESTRICTED);
+ // Get the correct apk paths while maintaining the current context's configuration.
+ return applicationContext.createConfigurationContext(
+ context.getResources().getConfiguration());
} catch (NameNotFoundException e) {
Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f9d553ffc0c1..ebd015f1c429 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.content.ContentProvider.getUriWithoutUserId;
import static android.content.ContentProvider.getUserIdFromUri;
import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
@@ -40,7 +41,9 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.IUriGrantsManager;
import android.app.SharedElementCallback;
+import android.app.UriGrantsManager;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
import android.app.prediction.AppPredictor;
@@ -77,6 +80,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
@@ -86,6 +90,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.os.PatternMatcher;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
@@ -692,7 +697,11 @@ public class ChooserActivity extends ResolverActivity implements
targets = null;
break;
}
- targets[i] = (ChooserTarget) pa[i];
+ ChooserTarget chooserTarget = (ChooserTarget) pa[i];
+ if (!hasValidIcon(chooserTarget)) {
+ chooserTarget = removeIcon(chooserTarget);
+ }
+ targets[i] = chooserTarget;
}
mCallerChooserTargets = targets;
}
@@ -4313,4 +4322,43 @@ public class ChooserActivity extends ResolverActivity implements
private boolean shouldNearbyShareBeIncludedAsActionButton() {
return !shouldNearbyShareBeFirstInRankedRow();
}
+
+ private boolean hasValidIcon(ChooserTarget target) {
+ Icon icon = target.getIcon();
+ if (icon == null) {
+ return true;
+ }
+ if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ Uri uri = icon.getUri();
+ try {
+ getUriGrantsManager().checkGrantUriPermission_ignoreNonSystem(
+ getLaunchedFromUid(),
+ getPackageName(),
+ getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ getUserIdFromUri(uri)
+ );
+ } catch (SecurityException | RemoteException e) {
+ Log.e(TAG, "Failed to get URI permission for: " + uri, e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private IUriGrantsManager getUriGrantsManager() {
+ return UriGrantsManager.getService();
+ }
+
+ private static ChooserTarget removeIcon(ChooserTarget target) {
+ if (target == null) {
+ return null;
+ }
+ return new ChooserTarget(
+ target.getTitle(),
+ null,
+ target.getScore(),
+ target.getComponentName(),
+ target.getIntentExtras());
+ }
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 65b59790e327..259e82181da1 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -512,24 +512,35 @@ public class IntentForwarderActivity extends Activity {
Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
sanitizeIntent(forwardIntent);
- Intent intentToCheck = forwardIntent;
- if (Intent.ACTION_CHOOSER.equals(forwardIntent.getAction())) {
+ if (!canForwardInner(forwardIntent, sourceUserId, targetUserId, packageManager,
+ contentResolver)) {
return null;
}
if (forwardIntent.getSelector() != null) {
- intentToCheck = forwardIntent.getSelector();
+ sanitizeIntent(forwardIntent.getSelector());
+ if (!canForwardInner(forwardIntent.getSelector(), sourceUserId, targetUserId,
+ packageManager, contentResolver)) {
+ return null;
+ }
+ }
+ return forwardIntent;
+ }
+
+ private static boolean canForwardInner(Intent intent, int sourceUserId, int targetUserId,
+ IPackageManager packageManager, ContentResolver contentResolver) {
+ if (Intent.ACTION_CHOOSER.equals(intent.getAction())) {
+ return false;
}
- String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
- sanitizeIntent(intentToCheck);
+ String resolvedType = intent.resolveTypeIfNeeded(contentResolver);
try {
if (packageManager.canForwardTo(
- intentToCheck, resolvedType, sourceUserId, targetUserId)) {
- return forwardIntent;
+ intent, resolvedType, sourceUserId, targetUserId)) {
+ return true;
}
} catch (RemoteException e) {
Slog.e(TAG, "PackageManagerService is dead?");
}
- return null;
+ return false;
}
/**
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 70013911904e..d1911e43f7ee 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6462,4 +6462,8 @@
<!-- Whether unlocking and waking a device are sequenced -->
<bool name="config_orderUnlockAndWake">false</bool>
+
+ <!-- List of protected packages that require biometric authentication for modification
+ (Disable, force-stop or uninstalling updates). -->
+ <string-array name="config_biometric_protected_package_names" translatable="false" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2425d367e1db..20c179289a20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5135,4 +5135,8 @@
<!-- Whether we order unlocking and waking -->
<java-symbol type="bool" name="config_orderUnlockAndWake" />
+
+ <!-- List of protected packages that require biometric authentication for modification -->
+ <java-symbol type="array" name="config_biometric_protected_package_names" />
+
</resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 229b7a73a2a3..e70acc314658 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -68,7 +68,7 @@ public class InstallStart extends Activity {
mUserManager = getSystemService(UserManager.class);
Intent intent = getIntent();
- String callingPackage = getCallingPackage();
+ String callingPackage = getLaunchedFromPackage();
String callingAttributionTag = null;
final boolean isSessionInstall =
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 9c67817cbef4..bf3bc8e7dcb3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -56,8 +56,6 @@ import com.android.packageinstaller.television.ErrorFragment;
import com.android.packageinstaller.television.UninstallAlertFragment;
import com.android.packageinstaller.television.UninstallAppProgress;
-import java.util.List;
-
/*
* This activity presents UI to uninstall an application. Usually launched with intent
* Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
@@ -162,12 +160,15 @@ public class UninstallerActivity extends Activity {
if (mDialogInfo.user == null) {
mDialogInfo.user = Process.myUserHandle();
} else {
- List<UserHandle> profiles = userManager.getUserProfiles();
- if (!profiles.contains(mDialogInfo.user)) {
- Log.e(TAG, "User " + Process.myUserHandle() + " can't request uninstall "
- + "for user " + mDialogInfo.user);
- showUserIsNotAllowed();
- return;
+ if (!mDialogInfo.user.equals(Process.myUserHandle())) {
+ final boolean isCurrentUserProfileOwner = Process.myUserHandle().equals(
+ userManager.getProfileParent(mDialogInfo.user));
+ if (!isCurrentUserProfileOwner) {
+ Log.e(TAG, "User " + Process.myUserHandle() + " can't request uninstall "
+ + "for user " + mDialogInfo.user);
+ showUserIsNotAllowed();
+ return;
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
new file mode 100644
index 000000000000..03c1f92aad4c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -0,0 +1,545 @@
+/*
+ * 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.qs.tiles;
+
+import static android.content.pm.PackageManager.FEATURE_NFC_HOST_CARD_EMULATION;
+import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.service.quickaccesswallet.GetWalletCardsError;
+import android.service.quickaccesswallet.GetWalletCardsResponse;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quickaccesswallet.QuickAccessWalletService;
+import android.service.quickaccesswallet.WalletCard;
+import android.service.quicksettings.Tile;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class QuickAccessWalletTileTest extends SysuiTestCase {
+
+ private static final String CARD_ID = "card_id";
+ private static final String LABEL = "QAW";
+ private static final String CARD_DESCRIPTION = "•••• 1234";
+ private static final Icon CARD_IMAGE =
+ Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
+ private static final Icon INVALID_CARD_IMAGE =
+ Icon.createWithContentUri("content://media/external/images/media");
+ private static final int PRIMARY_USER_ID = 0;
+ private static final int SECONDARY_USER_ID = 10;
+
+ private final Drawable mTileIcon = mContext.getDrawable(R.drawable.ic_qs_wallet);
+ private final Intent mWalletIntent = new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
+ .setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
+
+ @Mock
+ private QSHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSLogger mQSLogger;
+ @Mock
+ private QsEventLogger mUiEventLogger;
+ @Mock
+ private QuickAccessWalletClient mQuickAccessWalletClient;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private SecureSettings mSecureSettings;
+ @Mock
+ private QuickAccessWalletController mController;
+ @Mock
+ private Icon mCardImage;
+ @Captor
+ ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
+
+ private Context mSpiedContext;
+ private TestableLooper mTestableLooper;
+ private QuickAccessWalletTile mTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+ mSpiedContext = spy(mContext);
+
+ doNothing().when(mSpiedContext).startActivity(any(Intent.class));
+ when(mHost.getContext()).thenReturn(mSpiedContext);
+ when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL);
+ when(mQuickAccessWalletClient.getTileIcon()).thenReturn(mTileIcon);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
+ when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true);
+ when(mController.getWalletClient()).thenReturn(mQuickAccessWalletClient);
+ when(mCardImage.getType()).thenReturn(Icon.TYPE_URI);
+ when(mCardImage.loadDrawableAsUser(any(), eq(SECONDARY_USER_ID))).thenReturn(null);
+
+ mTile = new QuickAccessWalletTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mKeyguardStateController,
+ mPackageManager,
+ mSecureSettings,
+ mController);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+ }
+
+ @After
+ public void tearDown() {
+ mTile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void testNewTile() {
+ assertFalse(mTile.newTileState().handlesLongClick);
+ }
+
+ @Test
+ public void testWalletServiceUnavailable_recreateWalletClient() {
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+
+ mTile.handleSetListening(true);
+
+ verify(mController, times(1)).reCreateWalletClient();
+ }
+
+ @Test
+ public void testWalletFeatureUnavailable_recreateWalletClient() {
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false);
+
+ mTile.handleSetListening(true);
+
+ verify(mController, times(1)).reCreateWalletClient();
+ }
+
+ @Test
+ public void testIsAvailable_qawFeatureAvailableWalletUnavailable() {
+ when(mController.isWalletRoleAvailable()).thenReturn(false);
+ when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false);
+ when(mSecureSettings.getStringForUser(NFC_PAYMENT_DEFAULT_COMPONENT,
+ UserHandle.USER_CURRENT)).thenReturn("Component");
+
+ assertTrue(mTile.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_nfcUnavailableWalletAvailable() {
+ when(mController.isWalletRoleAvailable()).thenReturn(true);
+ when(mHost.getUserId()).thenReturn(PRIMARY_USER_ID);
+ when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(false);
+ when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false);
+
+ assertTrue(mTile.isAvailable());
+ }
+
+ @Test
+ public void testHandleClick_startQuickAccessUiIntent_noCard() {
+ setUpWalletCard(/* hasCard= */ false);
+
+ mTile.handleClick(/* view= */ null);
+ mTestableLooper.processAllMessages();
+
+ verify(mController).startQuickAccessUiIntent(
+ eq(mActivityStarter),
+ eq(null),
+ /* hasCard= */ eq(false));
+ }
+
+ @Test
+ public void testHandleClick_startQuickAccessUiIntent_hasCard() {
+ setUpWalletCard(/* hasCard= */ true);
+
+ mTile.handleClick(null /* view */);
+ mTestableLooper.processAllMessages();
+
+ verify(mController).startQuickAccessUiIntent(
+ eq(mActivityStarter),
+ eq(null),
+ /* hasCard= */ eq(true));
+ }
+
+ @Test
+ public void testHandleUpdateState_updateLabelAndIcon() {
+ QSTile.State state = new QSTile.State();
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(LABEL, state.label.toString());
+ assertTrue(state.label.toString().contentEquals(state.contentDescription));
+ assertEquals(mTileIcon, state.icon.getDrawable(mContext));
+ }
+
+ @Test
+ public void testHandleUpdateState_updateLabelAndIcon_noIconFromApi() {
+ when(mQuickAccessWalletClient.getTileIcon()).thenReturn(null);
+ QSTile.State state = new QSTile.State();
+ QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(LABEL, state.label.toString());
+ assertTrue(state.label.toString().contentEquals(state.contentDescription));
+ assertEquals(icon, state.icon);
+ }
+
+ @Test
+ public void testGetTileLabel_serviceLabelExists() {
+ assertEquals(LABEL, mTile.getTileLabel().toString());
+ }
+
+ @Test
+ public void testGetTileLabel_serviceLabelDoesNotExist() {
+ when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(null);
+ assertEquals(mContext.getString(R.string.wallet_title), mTile.getTileLabel().toString());
+ }
+
+ @Test
+ public void testHandleUpdateState_walletIsUpdating() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ QSTile.State state = new QSTile.State();
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ Collections.singletonList(createWalletCard(mContext)), 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ // Wallet cards fetching on its way; wallet updating.
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_INACTIVE, state.state);
+ assertEquals(
+ mContext.getString(R.string.wallet_secondary_label_updating), state.secondaryLabel);
+ assertNotNull(state.stateDescription);
+ assertNull(state.sideViewCustomDrawable);
+
+ // Wallet cards fetching completed.
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_ACTIVE, state.state);
+ assertEquals(CARD_DESCRIPTION, state.secondaryLabel);
+ assertNotNull(state.stateDescription);
+ assertNotNull(state.sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testHandleUpdateState_hasCard_deviceLocked_tileInactive() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+ QSTile.State state = new QSTile.State();
+ setUpWalletCard(/* hasCard= */ true);
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_INACTIVE, state.state);
+ assertEquals(CARD_DESCRIPTION, state.secondaryLabel);
+ assertNotNull(state.stateDescription);
+ assertNotNull(state.sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testHandleUpdateState_hasCard_deviceUnlocked_tileActive() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ QSTile.State state = new QSTile.State();
+ setUpWalletCard(/* hasCard= */ true);
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_ACTIVE, state.state);
+ assertEquals(CARD_DESCRIPTION, state.secondaryLabel);
+ assertNotNull(state.stateDescription);
+ assertNotNull(state.sideViewCustomDrawable);
+ }
+
+
+ @Test
+ public void testHandleUpdateState_noCard_tileInactive() {
+ QSTile.State state = new QSTile.State();
+ setUpWalletCard(/* hasCard= */ false);
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_INACTIVE, state.state);
+ assertEquals(
+ mContext.getString(R.string.wallet_secondary_label_no_card),
+ state.secondaryLabel);
+ assertNotNull(state.stateDescription);
+ assertNull(state.sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testHandleUpdateState_qawServiceUnavailable_tileUnavailable() {
+ when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+ QSTile.State state = new QSTile.State();
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_UNAVAILABLE, state.state);
+ assertNull(state.stateDescription);
+ assertNull(state.sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testHandleUpdateState_qawFeatureUnavailable_tileUnavailable() {
+ when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false);
+ QSTile.State state = new QSTile.State();
+
+ mTile.handleUpdateState(state, null);
+
+ assertEquals(Tile.STATE_UNAVAILABLE, state.state);
+ assertNull(state.stateDescription);
+ assertNull(state.sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testHandleSetListening_queryCards() {
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ assertThat(mCallbackCaptor.getValue()).isInstanceOf(
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class);
+ }
+
+ @Test
+ public void testQueryCards_hasCards_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCard(/* hasCard= */ true);
+
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_notCurrentUser_hasCards_noSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ WalletCard walletCard =
+ new WalletCard.Builder(
+ CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(Collections.singletonList(walletCard), 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_cardDataPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_PAYMENT);
+
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_cardDataNonPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_NON_PAYMENT);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_noCards_notUpdateSideViewDrawable() {
+ setUpWalletCard(/* hasCard= */ false);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_invalidDrawable_noSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpInvalidWalletCard(/* hasCard= */ true);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_error_notUpdateSideViewDrawable() {
+ String errorMessage = "getWalletCardsError";
+ GetWalletCardsError error = new GetWalletCardsError(CARD_IMAGE, errorMessage);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
+ mTestableLooper.processAllMessages();
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testHandleSetListening_notListening_notQueryCards() {
+ mTile.handleSetListening(false);
+
+ verifyNoMoreInteractions(mQuickAccessWalletClient);
+ }
+
+ private WalletCard createWalletCardWithType(Context context, int cardType) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(CARD_ID, cardType, CARD_IMAGE, CARD_DESCRIPTION,
+ pendingIntent).build();
+ }
+
+ private void setUpWalletCardWithType(boolean hasCard, int cardType) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(
+ createWalletCardWithType(mContext, cardType))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
+ private void setUpWalletCard(boolean hasCard) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(createWalletCard(mContext))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
+ private void setUpInvalidWalletCard(boolean hasCard) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(createInvalidWalletCard(mContext))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
+ private WalletCard createWalletCard(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(CARD_ID, CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
+ }
+
+ private WalletCard createInvalidWalletCard(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(
+ CARD_ID, INVALID_CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
+ }
+
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 7f706859abb3..c95a1b6ba46a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -802,6 +802,11 @@ public class AuthContainerView extends LinearLayout
}
@Override
+ public String getClassNameIfItIsConfirmDeviceCredentialActivity() {
+ return mConfig.mPromptInfo.getClassNameIfItIsConfirmDeviceCredentialActivity();
+ }
+
+ @Override
public void animateToCredentialUI() {
if (mBiometricView != null) {
mBiometricView.startTransitionToCredentialUI();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 57f1928fe545..ec132e4dec50 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -26,6 +26,7 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -243,28 +244,21 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mExecution.assertIsMainThread();
if (mCurrentDialog != null) {
try {
- final String clientPackage = mCurrentDialog.getOpPackageName();
- Log.w(TAG, "Task stack changed, current client: " + clientPackage);
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(clientPackage)
- && !Utils.isSystem(mContext, clientPackage)) {
- Log.e(TAG, "Evicting client due to: " + topPackage);
- mCurrentDialog.dismissWithoutCallback(true /* animate */);
- mCurrentDialog = null;
-
- for (Callback cb : mCallbacks) {
- cb.onBiometricPromptDismissed();
- }
+ if (isOwnerInBackground()) {
+ Log.w(TAG, "Evicting client due to top activity is not : "
+ + mCurrentDialog.getOpPackageName());
+ mCurrentDialog.dismissWithoutCallback(true /* animate */);
+ mCurrentDialog = null;
+
+ for (Callback cb : mCallbacks) {
+ cb.onBiometricPromptDismissed();
+ }
- if (mReceiver != null) {
- mReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
- null /* credentialAttestation */);
- mReceiver = null;
- }
+ if (mReceiver != null) {
+ mReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ null /* credentialAttestation */);
+ mReceiver = null;
}
}
} catch (RemoteException e) {
@@ -273,6 +267,45 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
}
}
+ private boolean isOwnerInBackground() {
+ if (mCurrentDialog != null) {
+ final String clientPackage = mCurrentDialog.getOpPackageName();
+
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (runningTasks == null || runningTasks.isEmpty()) {
+ Log.w(TAG, "No running tasks reported");
+ return false;
+ }
+
+ final boolean isSystemApp = Utils.isSystem(mContext, clientPackage);
+
+ final ComponentName topActivity = runningTasks.get(0).topActivity;
+ final String topPackage = topActivity.getPackageName();
+ final boolean topPackageEqualsToClient =
+ topPackage == null
+ || topActivity.getPackageName().contentEquals(clientPackage);
+
+ // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on
+ // class name.
+ final String clientClassNameForCDCA =
+ mCurrentDialog.getClassNameIfItIsConfirmDeviceCredentialActivity();
+ final boolean isClientCDCA = clientClassNameForCDCA != null;
+ final String topClassName = topActivity.getClassName();
+ final boolean isCDCAWithWrongTopClass =
+ isClientCDCA
+ && !(topClassName == null
+ || topClassName.contentEquals(clientClassNameForCDCA));
+
+ final boolean isInBackground =
+ !(isSystemApp || topPackageEqualsToClient) || isCDCAWithWrongTopClass;
+
+ Log.w(TAG, "isInBackground " + isInBackground);
+ return isInBackground;
+ }
+ return false;
+ }
+
/**
* Whether all fingerprint authentictors have been registered.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index b6eabfa76e36..d589608490ad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -160,6 +160,12 @@ public interface AuthDialog extends Dumpable {
long getRequestId();
/**
+ * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is
+ * not ConfirmDeviceCredentialActivity.
+ */
+ String getClassNameIfItIsConfirmDeviceCredentialActivity();
+
+ /**
* Animate to credential UI. Typically called after biometric is locked out.
*/
void animateToCredentialUI();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index f6a2f3704283..eb7698c42329 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -98,12 +98,11 @@ public class MediaProjectionPermissionActivity extends Activity
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
-
- mPackageName = getCallingPackage();
+ mPackageName = getLaunchedFromPackage();
// This activity is launched directly by an app, or system server. System server provides
// the package name through the intent if so.
- if (mPackageName == null) {
+ if (getCallingPackage() == null) {
if (launchingIntent.hasExtra(EXTRA_PACKAGE_REUSING_GRANTED_CONSENT)) {
mPackageName = launchingIntent.getStringExtra(
EXTRA_PACKAGE_REUSING_GRANTED_CONSENT);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 544e6ad295ff..6f29866e3ecb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -16,8 +16,13 @@
package com.android.systemui.qs.tiles;
-import static android.graphics.drawable.Icon.TYPE_URI;
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+import static android.graphics.drawable.Icon.TYPE_URI;
+import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_RESOURCE;
+import static android.graphics.drawable.Icon.TYPE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_ADAPTIVE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_DATA;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
@@ -226,11 +231,21 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
return;
}
mSelectedCard = cards.get(selectedIndex);
- android.graphics.drawable.Icon cardImageIcon = mSelectedCard.getCardImage();
- if (cardImageIcon.getType() == TYPE_URI) {
- mCardViewDrawable = null;
- } else {
- mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext);
+ switch (mSelectedCard.getCardImage().getType()) {
+ case TYPE_BITMAP:
+ case TYPE_ADAPTIVE_BITMAP:
+ mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext);
+ break;
+ case TYPE_URI:
+ case TYPE_URI_ADAPTIVE_BITMAP:
+ case TYPE_RESOURCE:
+ case TYPE_DATA:
+ mCardViewDrawable = null;
+ break;
+ default:
+ Log.e(TAG, "Unknown icon type: " + mSelectedCard.getCardImage().getType());
+ mCardViewDrawable = null;
+ break;
}
refreshState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 91c08a062b54..5c0b2a873620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -601,17 +601,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
}
- CharSequence title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
- CharSequence text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
- CharSequence ticker = n.tickerText;
-
- // Some apps just put the app name into the title
- CharSequence titleOrText = TextUtils.equals(title, appName) ? text : title;
-
- CharSequence desc = !TextUtils.isEmpty(titleOrText) ? titleOrText
- : !TextUtils.isEmpty(ticker) ? ticker : "";
-
- return c.getString(R.string.accessibility_desc_notification_icon, appName, desc);
+ return c.getString(R.string.accessibility_desc_notification_icon, appName, "");
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 81d04d4458c0..5627ce987aab 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -339,13 +339,19 @@ public class WalletScreenController implements
QAWalletCardViewInfo(Context context, WalletCard walletCard) {
mWalletCard = walletCard;
Icon cardImageIcon = mWalletCard.getCardImage();
- if (cardImageIcon.getType() == Icon.TYPE_URI) {
- mCardDrawable = null;
- } else {
+ if (cardImageIcon.getType() == Icon.TYPE_BITMAP
+ || cardImageIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
mCardDrawable = mWalletCard.getCardImage().loadDrawable(context);
+ } else {
+ mCardDrawable = null;
}
Icon icon = mWalletCard.getCardIcon();
- mIconDrawable = icon == null ? null : icon.loadDrawable(context);
+ if (icon != null && (icon.getType() == Icon.TYPE_BITMAP
+ || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP)) {
+ mIconDrawable = icon.loadDrawable(context);
+ } else {
+ mIconDrawable = null;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index cab47a3c4b4b..2bb493132b9d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -282,6 +282,11 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
return mCardLabel;
}
+ @VisibleForTesting
+ ImageView getIcon() {
+ return mIcon;
+ }
+
@Nullable
private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
Drawable icon = walletCard.getIcon();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 692af6a9a37b..f7b16e295a48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -316,6 +316,31 @@ public class WalletScreenControllerTest extends SysuiTestCase {
}
@Test
+ public void queryCards_hasCards_showCarousel_invalidIconSource_noIcon() {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ Collections.singletonList(createWalletCardWithInvalidIcon(mContext)), 0);
+
+ mController.queryWalletCards();
+ mTestableLooper.processAllMessages();
+
+ verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+ mCallbackCaptor.getValue();
+
+ assertEquals(mController, callback);
+
+ callback.onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(VISIBLE, mWalletView.getCardCarousel().getVisibility());
+ assertEquals(GONE, mWalletView.getEmptyStateView().getVisibility());
+ assertEquals(GONE, mWalletView.getErrorView().getVisibility());
+ assertEquals(null, mWalletView.getIcon().getDrawable());
+ }
+
+ @Test
public void queryCards_noCards_showEmptyState() {
GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
@@ -507,6 +532,16 @@ public class WalletScreenControllerTest extends SysuiTestCase {
.build();
}
+ private WalletCard createWalletCardWithInvalidIcon(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(
+ CARD_ID_1, createIconWithInvalidSource(), "•••• 1234", pendingIntent)
+ .setCardIcon(createIconWithInvalidSource())
+ .setCardLabel("Hold to reader")
+ .build();
+ }
+
private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) {
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
@@ -520,6 +555,10 @@ public class WalletScreenControllerTest extends SysuiTestCase {
return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888));
}
+ private static Icon createIconWithInvalidSource() {
+ return Icon.createWithContentUri("content://media/external/images/media");
+ }
+
private WalletCardViewInfo createCardViewInfo(WalletCard walletCard) {
return new WalletScreenController.QAWalletCardViewInfo(
mContext, walletCard);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 71ed5dbe8ab6..faebe7359eb5 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3124,6 +3124,12 @@ public class AccountManagerService
"the type and name should not be empty");
return;
}
+ if (!type.equals(mAccountType)) {
+ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "incorrect account type");
+ return;
+ }
+
Account resultAccount = new Account(name, type);
if (!customTokens) {
saveAuthTokenToDatabase(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 46adc79ed056..4f6b855ebd2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13804,8 +13804,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return null;
}
if (callerApp.info.uid != SYSTEM_UID
- && !callerApp.getPkgList().containsKey(callerPackage)
- && !"android".equals(callerPackage)) {
+ && !callerApp.getPkgList().containsKey(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e2388e2918ab..1211ee25bfd8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -215,6 +215,12 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
private static final int CURRENT_VERSION = 1;
+ /**
+ * The upper limit of total number of attributed op entries that can be returned in a binder
+ * transaction to avoid TransactionTooLargeException
+ */
+ private static final int NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD = 2000;
+
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
@@ -589,7 +595,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- /** Returned from {@link #verifyAndGetBypass(int, String, String, String, boolean)}. */
+ /** Returned from {@link #verifyAndGetBypass(int, String, String, int, String, boolean)}. */
private static final class PackageVerificationResult {
final RestrictionBypass bypass;
@@ -1434,6 +1440,8 @@ public class AppOpsService extends IAppOpsService.Stub {
Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid())
== PackageManager.PERMISSION_GRANTED;
+ int totalAttributedOpEntryCount = 0;
+
if (ops == null) {
resOps = new ArrayList<>();
for (int j = 0; j < pkgOps.size(); j++) {
@@ -1441,7 +1449,12 @@ public class AppOpsService extends IAppOpsService.Stub {
if (opRestrictsRead(curOp.op) && !shouldReturnRestrictedAppOps) {
continue;
}
- resOps.add(getOpEntryForResult(curOp));
+ if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+ break;
+ }
+ OpEntry opEntry = getOpEntryForResult(curOp);
+ resOps.add(opEntry);
+ totalAttributedOpEntryCount += opEntry.getAttributedOpEntries().size();
}
} else {
for (int j = 0; j < ops.length; j++) {
@@ -1453,10 +1466,21 @@ public class AppOpsService extends IAppOpsService.Stub {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(getOpEntryForResult(curOp));
+ if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+ break;
+ }
+ OpEntry opEntry = getOpEntryForResult(curOp);
+ resOps.add(opEntry);
+ totalAttributedOpEntryCount += opEntry.getAttributedOpEntries().size();
}
}
}
+
+ if (totalAttributedOpEntryCount > NUM_ATTRIBUTED_OP_ENTRY_THRESHOLD) {
+ Slog.w(TAG, "The number of attributed op entries has exceeded the threshold. This "
+ + "could be due to DoS attack from malicious apps. The result is throttled.");
+ }
+
return resOps;
}
@@ -2503,10 +2527,10 @@ public class AppOpsService extends IAppOpsService.Stub {
public int checkPackage(int uid, String packageName) {
Objects.requireNonNull(packageName);
try {
- verifyAndGetBypass(uid, packageName, null, null, true);
+ verifyAndGetBypass(uid, packageName, null, Process.INVALID_UID, null, true);
// When the caller is the system, it's possible that the packageName is the special
// one (e.g., "root") which isn't actually existed.
- if (resolveUid(packageName) == uid
+ if (resolveNonAppUid(packageName) == uid
|| (isPackageExisted(packageName)
&& !filterAppAccessUnlocked(packageName, UserHandle.getUserId(uid)))) {
return AppOpsManager.MODE_ALLOWED;
@@ -2636,7 +2660,7 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean shouldCollectMessage) {
PackageVerificationResult pvr;
try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
boolean wasNull = attributionTag == null;
if (!pvr.isAttributionTagValid) {
attributionTag = null;
@@ -3178,7 +3202,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int attributionChainId, boolean dryRun) {
PackageVerificationResult pvr;
try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
if (!pvr.isAttributionTagValid) {
attributionTag = null;
}
@@ -3671,13 +3695,17 @@ public class AppOpsService extends IAppOpsService.Stub {
private boolean isSpecialPackage(int callingUid, @Nullable String packageName) {
final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid, packageName);
return callingUid == Process.SYSTEM_UID
- || resolveUid(resolvedPackage) != Process.INVALID_UID;
+ || resolveNonAppUid(resolvedPackage) != Process.INVALID_UID;
}
private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
if (attributionSource.getUid() != Binder.getCallingUid()
&& attributionSource.isTrusted(mContext)) {
- return true;
+ // if there is a next attribution source, it must be trusted, as well.
+ if (attributionSource.getNext() == null
+ || attributionSource.getNext().isTrusted(mContext)) {
+ return true;
+ }
}
return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null)
@@ -3760,19 +3788,20 @@ public class AppOpsService extends IAppOpsService.Stub {
}
/**
- * @see #verifyAndGetBypass(int, String, String, String, boolean)
+ * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
*/
private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
@Nullable String attributionTag) {
- return verifyAndGetBypass(uid, packageName, attributionTag, null);
+ return verifyAndGetBypass(uid, packageName, attributionTag, Process.INVALID_UID, null);
}
/**
- * @see #verifyAndGetBypass(int, String, String, String, boolean)
+ * @see #verifyAndGetBypass(int, String, String, int, String, boolean)
*/
private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag, @Nullable String proxyPackageName) {
- return verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, false);
+ @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName) {
+ return verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName,
+ false);
}
/**
@@ -3783,14 +3812,15 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param uid The uid the package belongs to
* @param packageName The package the might belong to the uid
* @param attributionTag attribution tag or {@code null} if no need to verify
- * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
+ * @param proxyUid The proxy uid, from which the attribution tag is to be pulled
+ * @param proxyPackageName The proxy package, from which the attribution tag may be pulled
* @param suppressErrorLogs Whether to print to logcat about nonmatching parameters
*
* @return PackageVerificationResult containing {@link RestrictionBypass} and whether the
* attribution tag is valid
*/
private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag, @Nullable String proxyPackageName,
+ @Nullable String attributionTag, int proxyUid, @Nullable String proxyPackageName,
boolean suppressErrorLogs) {
if (uid == Process.ROOT_UID) {
// For backwards compatibility, don't check package name for root UID.
@@ -3834,34 +3864,47 @@ public class AppOpsService extends IAppOpsService.Stub {
int callingUid = Binder.getCallingUid();
- // Allow any attribution tag for resolvable uids
- int pkgUid;
+ // Allow any attribution tag for resolvable, non-app uids
+ int nonAppUid;
if (Objects.equals(packageName, "com.android.shell")) {
// Special case for the shell which is a package but should be able
// to bypass app attribution tag restrictions.
- pkgUid = Process.SHELL_UID;
+ nonAppUid = Process.SHELL_UID;
} else {
- pkgUid = resolveUid(packageName);
+ nonAppUid = resolveNonAppUid(packageName);
}
- if (pkgUid != Process.INVALID_UID) {
- if (pkgUid != UserHandle.getAppId(uid)) {
+ if (nonAppUid != Process.INVALID_UID) {
+ if (nonAppUid != UserHandle.getAppId(uid)) {
if (!suppressErrorLogs) {
Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
- + "Package \"" + packageName + "\" does not belong to uid " + uid
- + ".");
+ + "Package \"" + packageName + "\" does not belong to uid " + uid
+ + ".");
+ }
+ String otherUidMessage =
+ DEBUG ? " but it is really " + nonAppUid : " but it is not";
+ throw new SecurityException("Specified package \"" + packageName
+ + "\" under uid " + UserHandle.getAppId(uid) + otherUidMessage);
+ }
+ // We only allow bypassing the attribution tag verification if the proxy is a
+ // system app (or is null), in order to prevent abusive apps clogging the appops
+ // system with unlimited attribution tags via proxy calls.
+ boolean proxyIsSystemAppOrNull = true;
+ if (proxyPackageName != null) {
+ int proxyAppId = UserHandle.getAppId(proxyUid);
+ if (proxyAppId >= Process.FIRST_APPLICATION_UID) {
+ proxyIsSystemAppOrNull =
+ mPackageManagerInternal.isSystemPackage(proxyPackageName);
}
- String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
- throw new SecurityException("Specified package \"" + packageName + "\" under uid "
- + UserHandle.getAppId(uid) + otherUidMessage);
}
return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
- /* isAttributionTagValid */ true);
+ /* isAttributionTagValid */ proxyIsSystemAppOrNull);
}
int userId = UserHandle.getUserId(uid);
RestrictionBypass bypass = null;
boolean isAttributionTagValid = false;
+ int pkgUid = nonAppUid;
final long ident = Binder.clearCallingIdentity();
try {
PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
@@ -4613,7 +4656,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (nonpackageUid != -1) {
packageName = null;
} else {
- packageUid = resolveUid(packageName);
+ packageUid = resolveNonAppUid(packageName);
if (packageUid < 0) {
packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
@@ -5628,7 +5671,13 @@ public class AppOpsService extends IAppOpsService.Stub {
if (restricted && attrOp.isRunning()) {
attrOp.pause();
} else if (attrOp.isPaused()) {
- attrOp.resume();
+ RestrictionBypass bypass = verifyAndGetBypass(uid, ops.packageName, attrOp.tag)
+ .bypass;
+ if (!isOpRestrictedLocked(uid, code, ops.packageName, attrOp.tag,
+ bypass, false)) {
+ // Only resume if there are no other restrictions remaining on this op
+ attrOp.resume();
+ }
}
}
}
@@ -6077,7 +6126,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- private static int resolveUid(String packageName) {
+ private static int resolveNonAppUid(String packageName) {
if (packageName == null) {
return Process.INVALID_UID;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b62fd616d299..9752299bca01 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2346,6 +2346,7 @@ public class NotificationManagerService extends SystemService {
mPermissionHelper,
mNotificationChannelLogger,
mAppOps,
+ mUgmInternal,
new SysUiStatsEvent.BuilderFactory(),
mShowReviewPermissionsNotification);
mRankingHelper = new RankingHelper(getContext(),
@@ -5875,13 +5876,7 @@ public class NotificationManagerService extends SystemService {
final Uri originalSoundUri =
(originalChannel != null) ? originalChannel.getSound() : null;
if (soundUri != null && !Objects.equals(originalSoundUri, soundUri)) {
- Binder.withCleanCallingIdentity(() -> {
- mUgmInternal.checkGrantUriPermission(sourceUid, null,
- ContentProvider.getUriWithoutUserId(soundUri),
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- ContentProvider.getUserIdFromUri(soundUri,
- UserHandle.getUserId(sourceUid)));
- });
+ PermissionHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index c9a6c630d41b..672130eef969 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1406,19 +1406,14 @@ public final class NotificationRecord {
* {@link SecurityException} depending on target SDK of enqueuing app.
*/
private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
- if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+ if (mGrantableUris != null && mGrantableUris.contains(uri)) {
+ return; // already verified this URI
+ }
- // We can't grant Uri permissions from system
final int sourceUid = getSbn().getUid();
if (sourceUid == android.os.Process.SYSTEM_UID) return;
-
- final long ident = Binder.clearCallingIdentity();
try {
- // This will throw SecurityException if caller can't grant
- mUgmInternal.checkGrantUriPermission(sourceUid, null,
- ContentProvider.getUriWithoutUserId(uri),
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+ PermissionHelper.grantUriPermission(mUgmInternal, uri, sourceUid);
if (mGrantableUris == null) {
mGrantableUris = new ArraySet<>();
@@ -1438,8 +1433,6 @@ public final class NotificationRecord {
}
}
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 93c83e181ec1..e62613f92e1c 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -25,19 +25,25 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
+import com.android.server.uri.UriGrantsManagerInternal;
import java.util.Collections;
import java.util.HashSet;
@@ -58,7 +64,7 @@ public final class PermissionHelper {
private final IPermissionManager mPermManager;
public PermissionHelper(Context context, IPackageManager packageManager,
- IPermissionManager permManager) {
+ IPermissionManager permManager) {
mContext = context;
mPackageManager = packageManager;
mPermManager = permManager;
@@ -296,6 +302,19 @@ public final class PermissionHelper {
return false;
}
+ static void grantUriPermission(final UriGrantsManagerInternal ugmInternal, Uri uri,
+ int sourceUid) {
+ if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+ Binder.withCleanCallingIdentity(() -> {
+ // This will throw a SecurityException if the caller can't grant.
+ ugmInternal.checkGrantUriPermission(sourceUid, null,
+ ContentProvider.getUriWithoutUserId(uri),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+ });
+ }
+
public static class PackagePermission {
public final String packageName;
public final @UserIdInt int userId;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 4399a3ca46c5..1aa3fcf25a59 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -78,6 +78,7 @@ import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.notification.PermissionHelper.PackagePermission;
+import com.android.server.uri.UriGrantsManagerInternal;
import org.json.JSONArray;
import org.json.JSONException;
@@ -185,6 +186,7 @@ public class PreferencesHelper implements RankingConfig {
private final PermissionHelper mPermissionHelper;
private final NotificationChannelLogger mNotificationChannelLogger;
private final AppOpsManager mAppOps;
+ private final UriGrantsManagerInternal mUgmInternal;
private SparseBooleanArray mBadgingEnabled;
private SparseBooleanArray mBubblesEnabled;
@@ -201,6 +203,7 @@ public class PreferencesHelper implements RankingConfig {
ZenModeHelper zenHelper, PermissionHelper permHelper,
NotificationChannelLogger notificationChannelLogger,
AppOpsManager appOpsManager,
+ UriGrantsManagerInternal ugmInternal,
SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
boolean showReviewPermissionsNotification) {
mContext = context;
@@ -211,6 +214,7 @@ public class PreferencesHelper implements RankingConfig {
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
mStatsEventBuilderFactory = statsEventBuilderFactory;
+ mUgmInternal = ugmInternal;
mShowReviewPermissionsNotification = showReviewPermissionsNotification;
XML_VERSION = 4;
@@ -999,6 +1003,11 @@ public class PreferencesHelper implements RankingConfig {
}
clearLockedFieldsLocked(channel);
+ // Verify that the app has permission to read the sound Uri
+ // Only check for new channels, as regular apps can only set sound
+ // before creating. See: {@link NotificationChannel#setSound}
+ PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid);
+
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance || r.fixedImportance);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 78f1fa60b69f..f6253e1f5aae 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -67,6 +67,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
+import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -634,11 +635,11 @@ public class ComputerEngine implements Computer {
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
int callingUid, boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
- enforceCrossUserOrProfilePermission(callingUid,
+ enforceCrossUserOrProfilePermission(Binder.getCallingUid(),
userId,
false /*requireFullPermission*/,
false /*checkShell*/,
- "query intent receivers");
+ "query intent services");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
@@ -2155,10 +2156,10 @@ public class ComputerEngine implements Computer {
return true;
}
if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid);
}
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, callingUid);
}
/**
@@ -2174,6 +2175,11 @@ public class ComputerEngine implements Computer {
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean hasPermission(String permission, int uid) {
+ return mContext.checkPermission(permission, Process.INVALID_PID, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
public final boolean isCallerSameApp(String packageName, int uid) {
return isCallerSameApp(packageName, uid, false /* resolveIsolatedUid */);
}
@@ -4590,7 +4596,7 @@ public class ComputerEngine implements Computer {
final boolean listApex = (flags & MATCH_APEX) != 0;
enforceCrossUserPermission(
- callingUid,
+ Binder.getCallingUid(),
userId,
false /* requireFullPermission */,
false /* checkShell */,
@@ -4667,8 +4673,14 @@ public class ComputerEngine implements Computer {
int callingUid) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
- final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
- userId);
+
+ // Callers of this API may not always separate the userID and authority. Let's parse it
+ // before resolving
+ String authorityWithoutUserId = ContentProvider.getAuthorityWithoutUserId(name);
+ userId = ContentProvider.getUserIdFromAuthority(name, userId);
+
+ final ProviderInfo providerInfo = mComponentResolver.queryProvider(this,
+ authorityWithoutUserId, flags, userId);
boolean checkedGrants = false;
if (providerInfo != null) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -4682,7 +4694,7 @@ public class ComputerEngine implements Computer {
if (!checkedGrants) {
boolean enforceCrossUser = true;
- if (isAuthorityRedirectedForCloneProfile(name)) {
+ if (isAuthorityRedirectedForCloneProfile(authorityWithoutUserId)) {
final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
UserInfo userInfo = umInternal.getUserInfo(UserHandle.getUserId(callingUid));
@@ -5136,7 +5148,7 @@ public class ComputerEngine implements Computer {
@Override
public int getComponentEnabledSetting(@NonNull ComponentName component, int callingUid,
@UserIdInt int userId) {
- enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false /*requireFullPermission*/,
false /*checkShell*/, "getComponentEnabled");
return getComponentEnabledSettingInternal(component, callingUid, userId);
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 8815834f6b5c..0b3f6fd87013 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -282,7 +282,8 @@ public class UserRestrictionsUtils {
* in settings. So it is handled separately.
*/
private static final Set<String> DEFAULT_ENABLED_FOR_MANAGED_PROFILES = Sets.newArraySet(
- UserManager.DISALLOW_BLUETOOTH_SHARING
+ UserManager.DISALLOW_BLUETOOTH_SHARING,
+ UserManager.DISALLOW_DEBUGGING_FEATURES
);
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a88e2b06a4c3..50d15816b2ce 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3003,7 +3003,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
case KeyEvent.KEYCODE_I:
- if (down && event.isMetaPressed()) {
+ if (down && event.isMetaPressed() && isUserSetupComplete() && !keyguardOn) {
showSystemSettings();
return key_consumed;
}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 0c98fb5000d5..941d612676da 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -652,6 +652,10 @@ public class LockTaskController {
if (!isSystemCaller) {
task.mLockTaskUid = callingUid;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+ if (mLockTaskModeTasks.contains(task)) {
+ Slog.i(TAG_LOCKTASK, "Already locked.");
+ return;
+ }
// startLockTask() called by app, but app is not part of lock task allowlist. Show
// app pinning request. We will come back here with isSystemCaller true.
ProtoLog.w(WM_DEBUG_LOCKTASK, "Mode default, asking user");
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 2ac3125de961..6037fa55d78f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1240,16 +1240,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
// Update the input-sink (touch-blocking) state now that the animation is finished.
- SurfaceControl.Transaction inputSinkTransaction = null;
+ boolean scheduleAnimation = false;
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
if (ar == null || !ar.isVisible() || ar.getParent() == null) continue;
- if (inputSinkTransaction == null) {
- inputSinkTransaction = ar.mWmService.mTransactionFactory.get();
- }
- ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(inputSinkTransaction);
+ scheduleAnimation = true;
+ ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(ar.getPendingTransaction());
}
- if (inputSinkTransaction != null) inputSinkTransaction.apply();
+ // To apply pending transactions.
+ if (scheduleAnimation) mController.mAtm.mWindowManager.scheduleAnimationLocked();
// Always schedule stop processing when transition finishes because activities don't
// stop while they are in a transition thus their stop could still be pending.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1549677fdbb2..59ca6151639b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2663,13 +2663,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* Apply default restrictions that haven't been applied to a given admin yet.
*/
private void maybeSetDefaultRestrictionsForAdminLocked(int userId, ActiveAdmin admin) {
- Set<String> defaultRestrictions =
- UserRestrictionsUtils.getDefaultEnabledForManagedProfiles();
- if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
+ final Set<String> restrictionsToSet =
+ new ArraySet<>(UserRestrictionsUtils.getDefaultEnabledForManagedProfiles());
+ restrictionsToSet.removeAll(admin.defaultEnabledRestrictionsAlreadySet);
+ if (restrictionsToSet.isEmpty()) {
return; // The same set of default restrictions has been already applied.
}
if (isPolicyEngineForFinanceFlagEnabled()) {
- for (String restriction : defaultRestrictions) {
+ for (String restriction : restrictionsToSet) {
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
EnforcingAdmin.createEnterpriseEnforcingAdmin(
@@ -2678,9 +2679,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
new BooleanPolicyValue(true),
userId);
}
- admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
+ admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " +
- defaultRestrictions);
+ restrictionsToSet);
return;
}
@@ -2688,21 +2689,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (VERBOSE_LOG) {
Slogf.d(LOG_TAG, "Default enabled restrictions: "
- + defaultRestrictions
+ + restrictionsToSet
+ ". Restrictions already enabled: "
+ admin.defaultEnabledRestrictionsAlreadySet);
}
-
- final Set<String> restrictionsToSet = new ArraySet<>(defaultRestrictions);
- restrictionsToSet.removeAll(admin.defaultEnabledRestrictionsAlreadySet);
- if (!restrictionsToSet.isEmpty()) {
- for (final String restriction : restrictionsToSet) {
- admin.ensureUserRestrictions().putBoolean(restriction, true);
- }
- admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
- Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
- saveUserRestrictionsLocked(userId);
+ for (final String restriction : restrictionsToSet) {
+ admin.ensureUserRestrictions().putBoolean(restriction, true);
}
+ admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
+ Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
+ saveUserRestrictionsLocked(userId);
}
private void setDeviceOwnershipSystemPropertyLocked() {
@@ -10192,7 +10188,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
- if (isAdb(caller)) {
+ boolean isAdb = isAdb(caller);
+ if (isAdb) {
// Log profile owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
DevicePolicyEventLogger
@@ -10214,7 +10211,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
maybeSetDefaultRestrictionsForAdminLocked(userHandle, admin);
ensureUnknownSourcesRestrictionForProfileOwnerLocked(userHandle, admin,
true /* newOwner */);
+ if (isAdb) {
+ // DISALLOW_DEBUGGING_FEATURES is being added to newly-created
+ // work profile by default due to b/382064697 . This would have
+ // impacted certain CTS test flows when they interact with the
+ // work profile via ADB (for example installing an app into the
+ // work profile). Remove DISALLOW_DEBUGGING_FEATURES here to
+ // reduce the potential impact.
+ setLocalUserRestrictionInternal(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(who, userHandle),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, false, userHandle);
+ }
}
+
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
userHandle);
});
@@ -11131,7 +11140,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mOwners.hasDeviceOwner()) {
return false;
}
-
+
final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
return false;
@@ -11140,7 +11149,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (isManagedProfile(userId)) {
return false;
}
-
+
return true;
}
private void enforceCanQueryLockTaskLocked(ComponentName who, String callerPackageName) {
@@ -24460,7 +24469,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
});
}
-
+
private void migrateUserControlDisabledPackagesLocked() {
Binder.withCleanCallingIdentity(() -> {
List<UserInfo> users = mUserManager.getUsers();
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index f3ac7d55c5db..7c6f0943dd02 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -55,6 +55,7 @@ import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
@@ -385,6 +386,10 @@ class PackageManagerComponentLabelIconOverrideTest {
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
PackageManager.PERMISSION_GRANTED
}
+ whenever(this.checkPermission(
+ eq(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt())) {
+ PackageManager.PERMISSION_GRANTED
+ }
}
val mockSharedLibrariesImpl: SharedLibrariesImpl = mock {
whenever(this.snapshot()) { this@mock }
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 f203cfa9e442..63688d1242c6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3646,7 +3646,42 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+ mBinderService.updateNotificationChannelFromPrivilegedListener(
+ null, mPkg, Process.myUserHandle(), updatedNotificationChannel);
+
+ verify(mPreferencesHelper, times(1)).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
+
+ verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
+ public void
+ testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
+ throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(singletonList(mock(AssociationInfo.class)));
+ when(mPreferencesHelper.getNotificationChannel(eq(mPkg), anyInt(),
+ eq(mTestNotificationChannel.getId()), anyBoolean()))
+ .thenReturn(mTestNotificationChannel);
+
+ // Missing Uri permissions for the old channel sound
+ final Uri oldSoundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+ doThrow(new SecurityException("no access")).when(mUgmInternal)
+ .checkGrantUriPermission(eq(Process.myUid()), any(), eq(oldSoundUri),
+ anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+ // Has Uri permissions for the old channel sound
+ final Uri newSoundUri = Uri.parse("content://media/test/sound/uri");
+ final NotificationChannel updatedNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+ updatedNotificationChannel.setSound(newSoundUri,
+ updatedNotificationChannel.getAudioAttributes());
mBinderService.updateNotificationChannelFromPrivilegedListener(
null, PKG, Process.myUserHandle(), updatedNotificationChannel);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index fae92d9ac738..bcdbd227d248 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -36,6 +36,7 @@ import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -76,6 +77,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
import com.android.server.UiServiceTestCase;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -855,22 +857,19 @@ public class NotificationRecordTest extends UiServiceTestCase {
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.setSound(null, null);
Notification n = new Notification.Builder(mContext, channel.getId())
.setSmallIcon(Icon.createWithContentUri(Uri.parse("content://something")))
.build();
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.mUgmInternal = ugm;
- try {
- record.calculateGrantableUris();
- fail("App provided uri for p targeting app should throw exception");
- } catch (SecurityException e) {
- // expected
- }
+ assertThrows("App provided uri for p targeting app should throw exception",
+ SecurityException.class,
+ () -> new NotificationRecord(mMockContext, sbn, channel));
}
@Test
@@ -880,17 +879,17 @@ public class NotificationRecordTest extends UiServiceTestCase {
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.setSound(Uri.parse("content://something"), mock(AudioAttributes.class));
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.mUgmInternal = ugm;
- record.calculateGrantableUris();
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
}
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 48ad86da1bc5..53bdce6718d1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -29,6 +29,10 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
+import static android.content.ContentResolver.SCHEME_CONTENT;
+import static android.content.ContentResolver.SCHEME_FILE;
import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.os.UserHandle.USER_SYSTEM;
@@ -67,6 +71,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -98,6 +103,7 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -303,7 +309,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
resetZenModeHelper();
mAudioAttributes = new AudioAttributes.Builder()
@@ -645,7 +651,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -711,7 +717,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
@@ -789,7 +795,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -846,7 +852,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_permissionNotificationOff() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -903,7 +909,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -959,7 +965,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_oldXml_migration_NoUid() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"2\">\n"
@@ -992,7 +998,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testReadXml_newXml_noMigration_NoUid() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"3\">\n"
@@ -1024,7 +1030,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForNonBackup_postMigration() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
@@ -1110,7 +1116,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
@@ -1202,7 +1208,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false));
@@ -1287,7 +1293,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
@@ -1432,7 +1438,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(actualChannel.isSoundRestored());
}
-
/**
* Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
* handle its restore properly.
@@ -1498,7 +1503,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
new FileNotFoundException("")).thenReturn(resId);
mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -2444,8 +2449,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
-
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2475,7 +2479,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2499,7 +2503,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2553,7 +2557,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
resetZenModeHelper();
@@ -2566,7 +2570,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
resetZenModeHelper();
@@ -2663,6 +2667,61 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testCreateChannel_noSoundUriPermission_contentSchemeVerified() {
+ final Uri sound = Uri.parse(SCHEME_CONTENT + "://media/test/sound/uri");
+
+ doThrow(new SecurityException("no access")).when(mUgmInternal)
+ .checkGrantUriPermission(eq(UID_N_MR1), any(), eq(sound),
+ anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+ final NotificationChannel channel = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setSound(sound, mAudioAttributes);
+
+ assertThrows(SecurityException.class,
+ () -> mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel,
+ true, false, UID_N_MR1, false));
+ assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true))
+ .isNull();
+ }
+
+ @Test
+ public void testCreateChannel_noSoundUriPermission_fileSchemaIgnored() {
+ final Uri sound = Uri.parse(SCHEME_FILE + "://path/sound");
+
+ doThrow(new SecurityException("no access")).when(mUgmInternal)
+ .checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
+ anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+ final NotificationChannel channel = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setSound(sound, mAudioAttributes);
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+ false);
+ assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true)
+ .getSound()).isEqualTo(sound);
+ }
+
+ @Test
+ public void testCreateChannel_noSoundUriPermission_resourceSchemaIgnored() {
+ final Uri sound = Uri.parse(SCHEME_ANDROID_RESOURCE + "://resId/sound");
+
+ doThrow(new SecurityException("no access")).when(mUgmInternal)
+ .checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
+ anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+ final NotificationChannel channel = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setSound(sound, mAudioAttributes);
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+ false);
+ assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true)
+ .getSound()).isEqualTo(sound);
+ }
+
+ @Test
public void testPermanentlyDeleteChannels() throws Exception {
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -3669,7 +3728,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "</ranking>\n";
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3683,7 +3742,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3753,7 +3812,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3766,7 +3825,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3780,7 +3839,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3796,7 +3855,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3852,7 +3911,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3890,7 +3949,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -4548,7 +4607,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4568,7 +4627,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testNormalConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4588,7 +4647,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testNoConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4608,7 +4667,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_noTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
@@ -4628,7 +4687,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_twice() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
@@ -4643,7 +4702,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_recentTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
@@ -4662,7 +4721,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
parser.nextTag();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
mHelper.readXml(parser, true, USER_SYSTEM);
NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
@@ -4674,7 +4733,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testUnDelete_time() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
@@ -4696,7 +4755,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testDeleted_longTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger,
- mAppOpsManager, mStatsEventBuilderFactory, false);
+ mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false);
long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 1d14dc31fa26..587a2363fca2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -239,6 +239,11 @@ public class LockTaskControllerTest {
verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
// THEN screen pinning toast should be shown
verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
+
+ // WHEN the app calls startLockTaskMode while the Task is already locked
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+ // THEN a pinning request should NOT be shown
+ verify(mStatusBarManagerInternal, never()).showScreenPinningRequest(anyInt());
}
@Test
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index 5f0c8d729e74..31b84ff04b85 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,6 +41,7 @@ public final class StatusHints implements Parcelable {
private final CharSequence mLabel;
private Icon mIcon;
private final Bundle mExtras;
+ private static final String TAG = StatusHints.class.getSimpleName();
/**
* @hide
@@ -150,17 +152,37 @@ public final class StatusHints implements Parcelable {
// 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;
+ int callingUserId = callingUserHandle.getIdentifier();
+ int requestingUserId = getUserIdFromAuthority(
+ icon.getUri().getAuthority(), callingUserId);
+ if (callingUserId != requestingUserId) {
+ return null;
}
+
}
return icon;
}
+ /**
+ * Derives the user id from the authority or the default user id if none could be found.
+ * @param auth
+ * @param defaultUserId
+ * @return The user id from the given authority.
+ * @hide
+ */
+ public static int getUserIdFromAuthority(String auth, int defaultUserId) {
+ if (auth == null) return defaultUserId;
+ int end = auth.lastIndexOf('@');
+ if (end == -1) return defaultUserId;
+ String userIdString = auth.substring(0, end);
+ try {
+ return Integer.parseInt(userIdString);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Error parsing userId." + e);
+ return UserHandle.USER_NULL;
+ }
+ }
+
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeCharSequence(mLabel);