Snap for 7804438 from 373ed0127e to sc-qpr1-release
Change-Id: Iab248a7d2fb230d3125f237de7a9cfbd209d3cfb
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user