Snap for 7804438 from 373ed0127e to sc-qpr1-release

Change-Id: Iab248a7d2fb230d3125f237de7a9cfbd209d3cfb
This commit is contained in:
Android Build Coastguard Worker
2021-10-08 01:06:29 +00:00
12 changed files with 332 additions and 46 deletions

View File

@@ -155,7 +155,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"ଉପଯୋଗକର୍ତ୍ତା: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"କିଛି ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ ମାନ ସେଟ୍‌ ହୋଇଛି"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ଡିଫଲ୍ଟ ସେଟ୍‍ ହୋଇନାହିଁ"</string>
<string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ସେଟିଂସ୍"</string>
<string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ସେଟିଂସ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ୍‍-ଟୁ-ସ୍ପିଚ୍‍ ଆଉଟ୍‍ପୁଟ୍‌"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପିଚ୍‌ ରେଟ୍"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"ଲେଖା ପଢ଼ିବାର ବେଗ"</string>

View File

@@ -39,6 +39,7 @@ import static android.hardware.SensorPrivacyManager.Sources.OTHER;
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
@@ -195,7 +196,7 @@ public final class SensorPrivacyService extends SystemService {
private EmergencyCallHelper mEmergencyCallHelper;
private KeyguardManager mKeyguardManager;
private int mCurrentUser = -1;
private int mCurrentUser = USER_NULL;
public SensorPrivacyService(Context context) {
super(context);
@@ -228,9 +229,9 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void onUserStarting(TargetUser user) {
if (mCurrentUser == -1) {
if (mCurrentUser == USER_NULL) {
mCurrentUser = user.getUserIdentifier();
mSensorPrivacyServiceImpl.userSwitching(-1, user.getUserIdentifier());
mSensorPrivacyServiceImpl.userSwitching(USER_NULL, user.getUserIdentifier());
}
}
@@ -1294,13 +1295,13 @@ public final class SensorPrivacyService extends SystemService {
micState = isIndividualSensorPrivacyEnabledLocked(to, MICROPHONE);
camState = isIndividualSensorPrivacyEnabledLocked(to, CAMERA);
}
if (prevMicState != micState) {
if (from == USER_NULL || prevMicState != micState) {
mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState);
setGlobalRestriction(MICROPHONE, micState);
}
if (prevCamState != camState) {
if (from == USER_NULL || prevCamState != camState) {
mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState);
setGlobalRestriction(CAMERA, micState);
setGlobalRestriction(CAMERA, camState);
}
}

View File

@@ -125,6 +125,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private static final int MSG_IGNORE_PROXIMITY = 8;
private static final int MSG_STOP = 9;
private static final int MSG_UPDATE_BRIGHTNESS = 10;
private static final int MSG_UPDATE_RBC = 11;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -422,13 +423,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
private float mTemporaryAutoBrightnessAdjustment;
// Whether a reduce bright colors (rbc) change has been initiated by the user. We want to
// retain the current backlight level when rbc is toggled, since rbc additionally makes the
// screen appear dimmer using screen colors rather than backlight levels, and therefore we
// don't actually want to compensate for this by then in/decreasing the backlight when
// toggling this feature.
// Whether reduce bright colors (rbc) has been turned on, or a change in strength has been
// requested. We want to retain the current backlight level when rbc is toggled, since rbc
// additionally makes the screen appear dimmer using screen colors rather than backlight levels,
// and therefore we don't actually want to compensate for this by then in/decreasing the
// backlight when toggling this feature.
// This should be false during system start up.
private boolean mPendingUserRbcChange;
private boolean mPendingRbcOnOrChanged = false;
// Animators.
private ObjectAnimator mColorFadeOnAnimator;
@@ -564,23 +565,35 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void onReduceBrightColorsActivationChanged(boolean activated,
boolean userInitiated) {
applyReduceBrightColorsSplineAdjustment(userInitiated);
applyReduceBrightColorsSplineAdjustment(
/* rbcStrengthChanged= */ false, activated);
}
@Override
public void onReduceBrightColorsStrengthChanged(int strength) {
applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
applyReduceBrightColorsSplineAdjustment(
/* rbcStrengthChanged= */ true, /* justActivated= */ false);
}
});
if (active) {
applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
applyReduceBrightColorsSplineAdjustment(
/* rbcStrengthChanged= */ false, /* justActivated= */ false);
}
} else {
mCdsi = null;
}
}
private void applyReduceBrightColorsSplineAdjustment(boolean userInitiated) {
private void applyReduceBrightColorsSplineAdjustment(
boolean rbcStrengthChanged, boolean justActivated) {
final int strengthChanged = rbcStrengthChanged ? 1 : 0;
final int activated = justActivated ? 1 : 0;
mHandler.obtainMessage(MSG_UPDATE_RBC, strengthChanged, activated).sendToTarget();
sendUpdatePowerState();
}
private void handleRbcChanged(boolean strengthChanged, boolean justActivated) {
if (mBrightnessMapper == null) {
Log.w(TAG, "No brightness mapping available to recalculate splines");
return;
@@ -591,8 +604,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
mPendingUserRbcChange = userInitiated;
sendUpdatePowerState();
mPendingRbcOnOrChanged = strengthChanged || justActivated;
// Reset model if strength changed OR rbc is turned off
if (strengthChanged || !justActivated && mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.resetShortTermModel();
}
}
/**
@@ -926,7 +944,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void reloadReduceBrightColours() {
if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
applyReduceBrightColorsSplineAdjustment(
/* rbcStrengthChanged= */ false, /* justActivated= */ false);
}
}
@@ -2072,21 +2091,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return true;
}
// We want to return true if the user has set the screen brightness.
// If they have just turned RBC on (and therefore added that interaction to the curve),
// or changed the brightness another way, then we should return true.
private boolean updateUserSetScreenBrightness() {
final boolean brightnessSplineChanged = mPendingUserRbcChange;
if (mPendingUserRbcChange && !Float.isNaN(mCurrentScreenBrightnessSetting)) {
final boolean treatAsIfUserChanged = mPendingRbcOnOrChanged;
if (treatAsIfUserChanged && !Float.isNaN(mCurrentScreenBrightnessSetting)) {
mLastUserSetScreenBrightness = mCurrentScreenBrightnessSetting;
}
mPendingUserRbcChange = false;
mPendingRbcOnOrChanged = false;
if ((Float.isNaN(mPendingScreenBrightnessSetting)
|| mPendingScreenBrightnessSetting < 0.0f)) {
return brightnessSplineChanged;
return treatAsIfUserChanged;
}
if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
return brightnessSplineChanged;
return treatAsIfUserChanged;
}
setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
@@ -2428,6 +2450,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
handleSettingsChange(false /*userSwitch*/);
break;
case MSG_UPDATE_RBC:
final int strengthChanged = msg.arg1;
final int justActivated = msg.arg2;
handleRbcChanged(strengthChanged == 1, justActivated == 1);
break;
}
}
}

View File

@@ -209,6 +209,12 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
/**
* True if a permissions query has been issued and is being processed. Used to prevent too many
* queries from being issued by a single client at once.
*/
private AtomicBoolean mIsPermQueryIssued = new AtomicBoolean(false);
/*
* True if the application creating the client has the ACCESS_CONTEXT_HUB permission.
*/
@@ -240,11 +246,11 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private final IContextHubTransactionCallback mQueryPermsCallback =
new IContextHubTransactionCallback.Stub() {
@Override
public void onTransactionComplete(int result) {
}
public void onTransactionComplete(int result) {}
@Override
public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
mIsPermQueryIssued.set(false);
if (result != ContextHubTransaction.RESULT_SUCCESS && nanoAppStateList != null) {
Log.e(TAG, "Permissions query failed, but still received nanoapp state");
} else if (nanoAppStateList != null) {
@@ -656,9 +662,11 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* communicated with in the past.
*/
private void checkNanoappPermsAsync() {
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
mTransactionManager.addTransaction(transaction);
if (!mIsPermQueryIssued.getAndSet(true)) {
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
mTransactionManager.addTransaction(transaction);
}
}
private int updateNanoAppAuthState(

View File

@@ -557,11 +557,14 @@ final class DefaultPermissionGrantPolicy {
grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
// SetupWizard
grantPermissionsToSystemPackage(pm,
ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
CAMERA_PERMISSIONS);
final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId));
grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
grantPermissionsToSystemPackage(
pm, setupWizardPackage, userId, NEARBY_DEVICES_PERMISSIONS);
}
// Camera
grantPermissionsToSystemPackage(pm,

View File

@@ -30,6 +30,7 @@ import android.os.Process;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.ILegacyPermissionManager;
import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -187,10 +188,25 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu
private void verifyCallerCanCheckAccess(String packageName, String message, int pid, int uid) {
// If the check is being requested by an app then only allow the app to query its own
// access status.
boolean reportError = false;
int callingUid = mInjector.getCallingUid();
int callingPid = mInjector.getCallingPid();
if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID && (callingUid != uid
|| callingPid != pid)) {
reportError = true;
}
// If the query is against an app on the device, then the check should only be allowed if
// the provided uid matches that of the specified package.
if (packageName != null && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
int packageUid = mInjector.getPackageUidForUser(packageName, UserHandle.getUserId(uid));
if (uid != packageUid) {
EventLog.writeEvent(0x534e4554, "193441322",
UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID
? callingUid : uid, "Package uid mismatch");
reportError = true;
}
}
if (reportError) {
String response = String.format(
"Calling uid %d, pid %d cannot access for package %s (uid=%d, pid=%d): %s",
callingUid, callingPid, packageName, uid, pid, message);
@@ -385,12 +401,14 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu
@VisibleForTesting
public static class Injector {
private final Context mContext;
private final PackageManagerInternal mPackageManagerInternal;
/**
* Public constructor that accepts a {@code context} within which to operate.
*/
public Injector(@NonNull Context context) {
mContext = context;
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
/**
@@ -453,5 +471,12 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu
return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0,
UserHandle.getUserHandleForUid(uid));
}
/**
* Returns the uid for the specified {@code packageName} under the provided {@code userId}.
*/
public int getPackageUidForUser(String packageName, int userId) {
return mPackageManagerInternal.getPackageUid(packageName, 0, userId);
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<sensor-privacy persistence-version="1" version="1">
<user id="0" enabled="false">
<individual-sensor-privacy sensor="1" enabled="true" />
<individual-sensor-privacy sensor="2" enabled="true" />
</user>
</sensor-privacy>

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<sensor-privacy persistence-version="1" version="1">
<user id="0" enabled="false">
<individual-sensor-privacy sensor="1" enabled="true" />
<individual-sensor-privacy sensor="2" enabled="false" />
</user>
</sensor-privacy>

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<sensor-privacy persistence-version="1" version="1">
<user id="0" enabled="false">
<individual-sensor-privacy sensor="1" enabled="false" />
<individual-sensor-privacy sensor="2" enabled="true" />
</user>
</sensor-privacy>

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<sensor-privacy persistence-version="1" version="1">
<user id="0" enabled="false">
<individual-sensor-privacy sensor="1" enabled="false" />
<individual-sensor-privacy sensor="2" enabled="false" />
</user>
</sensor-privacy>

View File

@@ -16,12 +16,18 @@
package com.android.server.sensorprivacy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
@@ -33,8 +39,10 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
import com.android.server.SensorPrivacyService;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -44,6 +52,7 @@ import org.mockito.quality.Strictness;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.concurrent.CompletableFuture;
@RunWith(AndroidTestingRunner.class)
public class SensorPrivacyServiceMockingTest {
@@ -63,10 +72,21 @@ public class SensorPrivacyServiceMockingTest {
public static final String PERSISTENCE_FILE6 =
String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE =
"SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml";
public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE =
"SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml";
public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE =
"SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml";
public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE =
"SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml";
private Context mContext;
@Mock
private AppOpsManager mMockedAppOpsManager;
@Mock
private AppOpsManagerInternal mMockedAppOpsManagerInternal;
@Mock
private UserManagerInternal mMockedUserManagerInternal;
@Mock
private ActivityManager mMockedActivityManager;
@@ -134,13 +154,103 @@ public class SensorPrivacyServiceMockingTest {
}
}
@Test
public void testServiceInit_AppOpsRestricted_micMute_camMute() throws IOException {
testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE, true, true);
}
@Test
public void testServiceInit_AppOpsRestricted_micMute_camUnmute() throws IOException {
testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE, true, false);
}
@Test
public void testServiceInit_AppOpsRestricted_micUnmute_camMute() throws IOException {
testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE, false, true);
}
@Test
public void testServiceInit_AppOpsRestricted_micUnmute_camUnmute() throws IOException {
testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE, false, false);
}
private void testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute,
boolean expectedMicState, boolean expectedCamState)
throws IOException {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.WARN)
.spyStatic(LocalServices.class)
.spyStatic(Environment.class)
.startMocking();
try {
mContext = InstrumentationRegistry.getInstrumentation().getContext();
spyOn(mContext);
doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
doReturn(mMockedAppOpsManagerInternal)
.when(() -> LocalServices.getService(AppOpsManagerInternal.class));
doReturn(mMockedUserManagerInternal)
.when(() -> LocalServices.getService(UserManagerInternal.class));
doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
doReturn(mMockedActivityTaskManager)
.when(mContext).getSystemService(ActivityTaskManager.class);
doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
TelephonyManager.class);
String dataDir = mContext.getApplicationInfo().dataDir;
doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
onDeviceFile.delete();
doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
.getUserInfo(0);
CompletableFuture<Boolean> micState = new CompletableFuture<>();
CompletableFuture<Boolean> camState = new CompletableFuture<>();
doAnswer(invocation -> {
int code = invocation.getArgument(0);
boolean restricted = invocation.getArgument(1);
if (code == AppOpsManager.OP_RECORD_AUDIO) {
micState.complete(restricted);
} else if (code == AppOpsManager.OP_CAMERA) {
camState.complete(restricted);
}
return null;
}).when(mMockedAppOpsManagerInternal).setGlobalRestriction(anyInt(), anyBoolean(),
any());
initServiceWithPersistenceFile(onDeviceFile, persistenceFileMicMuteCamMute, 0);
Assert.assertTrue(micState.join() == expectedMicState);
Assert.assertTrue(camState.join() == expectedCamState);
} finally {
mockitoSession.finishMocking();
}
}
private void initServiceWithPersistenceFile(File onDeviceFile,
String persistenceFilePath) throws IOException {
initServiceWithPersistenceFile(onDeviceFile, persistenceFilePath, -1);
}
private void initServiceWithPersistenceFile(File onDeviceFile,
String persistenceFilePath, int startingUserId) throws IOException {
if (persistenceFilePath != null) {
Files.copy(mContext.getAssets().open(persistenceFilePath),
onDeviceFile.toPath());
}
new SensorPrivacyService(mContext);
SensorPrivacyService service = new SensorPrivacyService(mContext);
if (startingUserId != -1) {
SystemService.TargetUser mockedTargetUser =
ExtendedMockito.mock(SystemService.TargetUser.class);
doReturn(startingUserId).when(mockedTargetUser).getUserIdentifier();
service.onUserStarting(mockedTargetUser);
}
onDeviceFile.delete();
}
}

View File

@@ -125,7 +125,7 @@ public class LegacyPermissionManagerServiceTest {
public void checkDeviceIdentifierAccess_hasPrivilegedPermission_returnsGranted() {
// Apps with the READ_PRIVILEGED_PHONE_STATE permission should have access to device
// identifiers.
setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
APP_PID, APP_UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -140,7 +140,7 @@ public class LegacyPermissionManagerServiceTest {
public void checkDeviceIdentifierAccess_hasAppOp_returnsGranted() {
// Apps that have been granted the READ_DEVICE_IDENTIFIERS appop should have access to
// device identifiers.
setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS),
eq(APP_UID), eq(mPackageName), any(), any())).thenReturn(
AppOpsManager.MODE_ALLOWED);
@@ -156,7 +156,7 @@ public class LegacyPermissionManagerServiceTest {
public void checkDeviceIdentifierAccess_hasDpmAccess_returnsGranted() {
// Apps that pass a DevicePolicyManager device / profile owner check should have access to
// device identifiers.
setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mDevicePolicyManager.hasDeviceIdentifierAccess(mPackageName, APP_PID,
APP_UID)).thenReturn(true);
@@ -236,7 +236,7 @@ public class LegacyPermissionManagerServiceTest {
// both the permission and the appop must be granted. If the permission is granted but the
// appop is not then AppOpsManager#MODE_IGNORED should be returned to indicate that this
// should be a silent failure.
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID);
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
setPackageTargetSdk(Build.VERSION_CODES.Q);
grantPermissionAndAppop(android.Manifest.permission.READ_PHONE_STATE, null);
@@ -256,7 +256,7 @@ public class LegacyPermissionManagerServiceTest {
// Apps targeting R+ with just the READ_PHONE_STATE permission granted should not have
// access to the phone number; PERMISSION_DENIED should be returned both with and without
// the appop granted since this check should be skipped for target SDK R+.
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID);
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
grantPermissionAndAppop(android.Manifest.permission.READ_PHONE_STATE, null);
int resultWithoutAppop = mLegacyPermissionManagerService.checkPhoneNumberAccess(
@@ -319,12 +319,79 @@ public class LegacyPermissionManagerServiceTest {
assertEquals(PackageManager.PERMISSION_GRANTED, resultWithAppop);
}
@Test
public void checkPhoneNumberAccess_providedUidDoesNotMatchPackageUid_throwsException()
throws Exception {
// An app can directly interact with one of the services that accepts a package name and
// returns a protected resource via a direct binder transact. This app could then provide
// the name of another app that targets pre-R, then determine if the app is installed based
// on whether the service throws an exception or not. While the app can provide the package
// name of another app, it cannot specify the package uid which is passed to the
// LegacyPermissionManager using Binder#getCallingUid. Ultimately this uid should then be
// compared against the actual uid of the package to ensure information about packages
// installed on the device is not leaked.
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID + 1);
assertThrows(SecurityException.class,
() -> mLegacyPermissionManagerService.checkPhoneNumberAccess(mPackageName,
CHECK_PHONE_NUMBER_MESSAGE, null, APP_PID, APP_UID));
}
@Test
public void checkPhoneNumberAccess_nullPackageNameSystemUid_returnsGranted() throws Exception {
// The platform can pass a null package name when checking if the platform itself has
// access to the device phone number(s) / identifier(s). This test ensures if a null package
// is provided, then the package uid check is skipped and the test is based on whether the
// the provided uid / pid has been granted the privileged permission.
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, -1);
when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
SYSTEM_PID, SYSTEM_UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
int result = mLegacyPermissionManagerService.checkPhoneNumberAccess(null,
CHECK_PHONE_NUMBER_MESSAGE, null, SYSTEM_PID, SYSTEM_UID);
assertEquals(PackageManager.PERMISSION_GRANTED, result);
}
@Test
public void checkPhoneNumberAccess_systemUidMismatchPackageUid_returnsGranted()
throws Exception {
// When the platform is checking device phone number / identifier access checks for other
// components on the platform, a uid less than the first application UID is provided; this
// test verifies the package uid check is skipped and access is still granted with the
// privileged permission.
int telephonyUid = SYSTEM_UID + 1;
int telephonyPid = SYSTEM_PID + 1;
setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, -1);
when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
telephonyPid, telephonyUid)).thenReturn(PackageManager.PERMISSION_GRANTED);
int result = mLegacyPermissionManagerService.checkPhoneNumberAccess(mPackageName,
CHECK_PHONE_NUMBER_MESSAGE, null, telephonyPid, telephonyUid);
assertEquals(PackageManager.PERMISSION_GRANTED, result);
}
/**
* Configures device identifier access tests to fail; tests verifying access should individually
* set an access check to succeed to verify access when that condition is met.
*/
private void setupCheckDeviceIdentifierAccessTest(int callingPid, int callingUid) {
setupAccessTest(callingPid, callingUid);
setupCheckDeviceIdentifierAccessTest(callingPid, callingUid, callingUid);
}
/**
* Configures device identifier access tests to fail; tests verifying access should individually
* set an access check to succeed to verify access when that condition is met.
*
* <p>To prevent leaking package information, access checks for package UIDs >= {@link
* android.os.Process#FIRST_APPLICATION_UID} must ensure the provided uid matches the uid of
* the package being checked; to ensure this check is successful, this method accepts the
* {@code packageUid} to be used for the package being checked.
*/
public void setupCheckDeviceIdentifierAccessTest(int callingPid, int callingUid,
int packageUid) {
setupAccessTest(callingPid, callingUid, packageUid);
when(mDevicePolicyManager.hasDeviceIdentifierAccess(anyString(), anyInt(),
anyInt())).thenReturn(false);
@@ -333,11 +400,26 @@ public class LegacyPermissionManagerServiceTest {
}
/**
* Configures phone number access tests to fail; tests verifying access should individually set
* an access check to succeed to verify access when that condition is met.
* Configures phone number access tests to fail; tests verifying access should individually
* set an access check to succeed to verify access when that condition is set.
*
*/
private void setupCheckPhoneNumberAccessTest(int callingPid, int callingUid) throws Exception {
setupAccessTest(callingPid, callingUid);
setupCheckPhoneNumberAccessTest(callingPid, callingUid, callingUid);
}
/**
* Configures phone number access tests to fail; tests verifying access should individually set
* an access check to succeed to verify access when that condition is met.
*
* <p>To prevent leaking package information, access checks for package UIDs >= {@link
* android.os.Process#FIRST_APPLICATION_UID} must ensure the provided uid matches the uid of
* the package being checked; to ensure this check is successful, this method accepts the
* {@code packageUid} to be used for the package being checked.
*/
private void setupCheckPhoneNumberAccessTest(int callingPid, int callingUid, int packageUid)
throws Exception {
setupAccessTest(callingPid, callingUid, packageUid);
setPackageTargetSdk(Build.VERSION_CODES.R);
}
@@ -345,9 +427,10 @@ public class LegacyPermissionManagerServiceTest {
* Configures the common mocks for any access tests using the provided {@code callingPid}
* and {@code callingUid}.
*/
private void setupAccessTest(int callingPid, int callingUid) {
private void setupAccessTest(int callingPid, int callingUid, int packageUid) {
when(mInjector.getCallingPid()).thenReturn(callingPid);
when(mInjector.getCallingUid()).thenReturn(callingUid);
when(mInjector.getPackageUidForUser(anyString(), anyInt())).thenReturn(packageUid);
when(mInjector.checkPermission(anyString(), anyInt(), anyInt())).thenReturn(
PackageManager.PERMISSION_DENIED);