blob: b1676d0e545ffb29955e26d06e1c8ed438c77138 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Process;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.ILegacyPermissionManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.UserManagerService;
/**
* Legacy permission manager service.
*/
public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stub {
private static final String TAG = "PackageManager";
/** Injector that can be used to facilitate testing. */
private final Injector mInjector;
@NonNull
private final Context mContext;
@NonNull
private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
/**
* Get or create an instance of this class for use by other components.
* <p>
* This method is not thread-safe.
*
* @param context the {@link Context}
* @return the internal instance
*/
@NonNull
public static LegacyPermissionManagerInternal create(@NonNull Context context) {
LegacyPermissionManagerInternal legacyPermissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
if (legacyPermissionManagerInternal == null) {
new LegacyPermissionManagerService(context);
legacyPermissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
}
return legacyPermissionManagerInternal;
}
private LegacyPermissionManagerService(@NonNull Context context) {
this(context, new Injector(context));
LocalServices.addService(LegacyPermissionManagerInternal.class, new Internal());
ServiceManager.addService("legacy_permission", this);
}
@VisibleForTesting
LegacyPermissionManagerService(@NonNull Context context, @NonNull Injector injector) {
mContext = context;
mInjector = injector;
mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(context);
}
@Override
public int checkDeviceIdentifierAccess(@Nullable String packageName, @Nullable String message,
@Nullable String callingFeatureId, int pid, int uid) {
verifyCallerCanCheckAccess(packageName, message, pid, uid);
// Allow system and root access to the device identifiers.
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Allow access to packages that have the READ_PRIVILEGED_PHONE_STATE permission.
if (mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid,
uid) == PackageManager.PERMISSION_GRANTED) {
return PackageManager.PERMISSION_GRANTED;
}
// If the calling package is not null then perform the appop and device / profile owner
// check.
if (packageName != null) {
// Allow access to a package that has been granted the READ_DEVICE_IDENTIFIERS appop.
final long token = mInjector.clearCallingIdentity();
AppOpsManager appOpsManager = (AppOpsManager) mInjector.getSystemService(
Context.APP_OPS_SERVICE);
try {
if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
packageName, callingFeatureId, message) == AppOpsManager.MODE_ALLOWED) {
return PackageManager.PERMISSION_GRANTED;
}
} finally {
mInjector.restoreCallingIdentity(token);
}
// Check if the calling packages meets the device / profile owner requirements for
// identifier access.
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) mInjector.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (devicePolicyManager != null && devicePolicyManager.hasDeviceIdentifierAccess(
packageName, pid, uid)) {
return PackageManager.PERMISSION_GRANTED;
}
}
return PackageManager.PERMISSION_DENIED;
}
@Override
public int checkPhoneNumberAccess(@Nullable String packageName, @Nullable String message,
@Nullable String callingFeatureId, int pid, int uid) {
verifyCallerCanCheckAccess(packageName, message, pid, uid);
if (mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid,
uid) == PackageManager.PERMISSION_GRANTED) {
// Skip checking for runtime permission since caller has privileged permission
return PackageManager.PERMISSION_GRANTED;
}
// if the packageName is null then just return now as the rest of the checks require a
// valid package name.
if (packageName == null) {
return PackageManager.PERMISSION_DENIED;
}
// If the target SDK version is below R then also check for READ_PHONE_STATE; prior to R
// the phone number was accessible with the READ_PHONE_STATE permission granted.
boolean preR = false;
int result = PackageManager.PERMISSION_DENIED;
try {
ApplicationInfo info = mInjector.getApplicationInfo(packageName, uid);
preR = info.targetSdkVersion <= Build.VERSION_CODES.Q;
} catch (PackageManager.NameNotFoundException nameNotFoundException) {
}
if (preR) {
// For target SDK < R if the READ_PHONE_STATE permission is granted but the appop
// is not granted then the caller should receive null / empty data instead of a
// potentially crashing SecurityException. Save the result of the READ_PHONE_STATE
// permission / appop check; if both do not pass then first check if the app meets
// any of the other requirements for access, if not then return the result of this
// check.
result = checkPermissionAndAppop(packageName,
android.Manifest.permission.READ_PHONE_STATE,
AppOpsManager.OPSTR_READ_PHONE_STATE, callingFeatureId, message, pid, uid);
if (result == PackageManager.PERMISSION_GRANTED) {
return result;
}
}
// Default SMS app can always read it.
if (checkPermissionAndAppop(packageName, null, AppOpsManager.OPSTR_WRITE_SMS,
callingFeatureId, message, pid, uid) == PackageManager.PERMISSION_GRANTED) {
return PackageManager.PERMISSION_GRANTED;
}
// Can be read with READ_PHONE_NUMBERS too.
if (checkPermissionAndAppop(packageName, android.Manifest.permission.READ_PHONE_NUMBERS,
AppOpsManager.OPSTR_READ_PHONE_NUMBERS, callingFeatureId, message, pid, uid)
== PackageManager.PERMISSION_GRANTED) {
return PackageManager.PERMISSION_GRANTED;
}
// Can be read with READ_SMS too.
if (checkPermissionAndAppop(packageName, android.Manifest.permission.READ_SMS,
AppOpsManager.OPSTR_READ_SMS, callingFeatureId, message, pid, uid)
== PackageManager.PERMISSION_GRANTED) {
return PackageManager.PERMISSION_GRANTED;
}
return result;
}
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.
int callingUid = mInjector.getCallingUid();
int callingPid = mInjector.getCallingPid();
if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID && (callingUid != uid
|| callingPid != pid)) {
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);
Log.w(TAG, response);
throw new SecurityException(response);
}
}
/**
* Returns whether the specified {@code packageName} with {@code pid} and {@code uid} has been
* granted the provided {@code permission} and {@code appop}, using the {@code callingFeatureId}
* and {@code message} for the {@link
* AppOpsManager#noteOpNoThrow(int, int, String, String, String)} call.
* @return <ul>
* <li>{@link PackageManager#PERMISSION_GRANTED} if both the permission and the appop
* are granted to the package</li>
* <li>{@link android.app.AppOpsManager#MODE_IGNORED} if the permission is granted to the
* package but the appop is not</li>
* <li>{@link PackageManager#PERMISSION_DENIED} if the permission is not granted to the
* package</li>
* </ul>
*/
private int checkPermissionAndAppop(String packageName, String permission, String appop,
String callingFeatureId, String message, int pid, int uid) {
if (permission != null) {
if (mInjector.checkPermission(permission, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
return PackageManager.PERMISSION_DENIED;
}
}
AppOpsManager appOpsManager = (AppOpsManager) mInjector.getSystemService(
Context.APP_OPS_SERVICE);
if (appOpsManager.noteOpNoThrow(appop, uid, packageName, callingFeatureId, message)
!= AppOpsManager.MODE_ALLOWED) {
return AppOpsManager.MODE_IGNORED;
}
return PackageManager.PERMISSION_GRANTED;
}
@Override
public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"grantDefaultPermissionsToActiveLuiApp", callingUid);
Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
.grantDefaultPermissionsToActiveLuiApp(packageName, userId));
}
@Override
public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"revokeDefaultPermissionsFromLuiApps", callingUid);
Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
.revokeDefaultPermissionsFromLuiApps(packageNames, userId));
}
@Override
public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"grantDefaultPermissionsToEnabledImsServices", callingUid);
Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
.grantDefaultPermissionsToEnabledImsServices(packageNames, userId));
}
@Override
public void grantDefaultPermissionsToEnabledTelephonyDataServices(
String[] packageNames, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid);
Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
.grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId));
}
@Override
public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
String[] packageNames, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid);
Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
.revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames,
userId));
}
@Override
public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"grantPermissionsToEnabledCarrierApps", callingUid);
Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId));
}
private class Internal implements LegacyPermissionManagerInternal {
@Override
public void resetRuntimePermissions() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
"revokeRuntimePermission");
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"resetRuntimePermissions");
}
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
final PermissionManagerServiceInternal permissionManagerInternal =
LocalServices.getService(PermissionManagerServiceInternal.class);
for (final int userId : UserManagerService.getInstance().getUserIds()) {
packageManagerInternal.forEachPackage(pkg ->
permissionManagerInternal.resetRuntimePermissions(pkg, userId));
}
}
@Override
public void setDialerAppPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider);
}
@Override
public void setLocationExtraPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider);
}
@Override
public void setLocationPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider);
}
@Override
public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider);
}
@Override
public void setSmsAppPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider);
}
@Override
public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider);
}
@Override
public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider);
}
@Override
public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider);
}
@Override
public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp(
packageName, userId);
}
@Override
public void grantDefaultPermissions(int userId) {
mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
}
@Override
public void scheduleReadDefaultPermissionExceptions() {
mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions();
}
}
/**
* Allows injection of services and method responses to facilitate testing.
*
* <p>Test classes can create a mock of this class and pass it to the PermissionManagerService
* constructor to control behavior of services and external methods during execution.
* @hide
*/
@VisibleForTesting
public static class Injector {
private final Context mContext;
/**
* Public constructor that accepts a {@code context} within which to operate.
*/
public Injector(@NonNull Context context) {
mContext = context;
}
/**
* Returns the UID of the calling package.
*/
public int getCallingUid() {
return Binder.getCallingUid();
}
/**
* Returns the process ID of the calling package.
*/
public int getCallingPid() {
return Binder.getCallingPid();
}
/**
* Checks if the package running under the specified {@code pid} and {@code uid} has been
* granted the provided {@code permission}.
*
* @return {@link PackageManager#PERMISSION_GRANTED} if the package has been granted the
* permission, {@link PackageManager#PERMISSION_DENIED} otherwise
*/
public int checkPermission(@NonNull String permission, int pid, int uid) {
return mContext.checkPermission(permission, pid, uid);
}
/**
* Clears the calling identity to allow subsequent calls to be treated as coming from this
* package.
*
* @return a token that can be used to restore the calling identity
*/
public long clearCallingIdentity() {
return Binder.clearCallingIdentity();
}
/**
* Restores the calling identity to that of the calling package based on the provided
* {@code token}.
*/
public void restoreCallingIdentity(long token) {
Binder.restoreCallingIdentity(token);
}
/**
* Returns the system service with the provided {@code name}.
*/
public Object getSystemService(@NonNull String name) {
return mContext.getSystemService(name);
}
/**
* Returns the {@link ApplicationInfo} for the specified {@code packageName} under the
* provided {@code uid}.
*/
public ApplicationInfo getApplicationInfo(@Nullable String packageName, int uid)
throws PackageManager.NameNotFoundException {
return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0,
UserHandle.getUserHandleForUid(uid));
}
}
}