1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.media; 18 19 import static android.Manifest.permission.ACCESS_MEDIA_LOCATION; 20 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; 21 import static android.Manifest.permission.WRITE_MEDIA_STORAGE; 22 import static android.app.AppOpsManager.MODE_ALLOWED; 23 import static android.app.AppOpsManager.OP_LEGACY_STORAGE; 24 import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE; 25 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 26 27 import android.app.AppGlobals; 28 import android.app.AppOpsManager; 29 import android.content.ContentProvider; 30 import android.content.Context; 31 import android.content.PermissionChecker; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageManager.NameNotFoundException; 34 import android.os.Binder; 35 import android.os.Build; 36 import android.os.storage.StorageManager; 37 38 import com.android.internal.util.ArrayUtils; 39 40 import libcore.util.EmptyArray; 41 42 public class LocalCallingIdentity { 43 public final int pid; 44 public final int uid; 45 public final String packageNameUnchecked; 46 LocalCallingIdentity(int pid, int uid, String packageNameUnchecked)47 private LocalCallingIdentity(int pid, int uid, String packageNameUnchecked) { 48 this.pid = pid; 49 this.uid = uid; 50 this.packageNameUnchecked = packageNameUnchecked; 51 } 52 fromBinder(ContentProvider provider)53 public static LocalCallingIdentity fromBinder(ContentProvider provider) { 54 String callingPackage = provider.getCallingPackageUnchecked(); 55 if (callingPackage == null) { 56 callingPackage = AppGlobals.getInitialApplication().getOpPackageName(); 57 } 58 return new LocalCallingIdentity(Binder.getCallingPid(), Binder.getCallingUid(), 59 callingPackage); 60 } 61 fromExternal(int uid, String packageName)62 public static LocalCallingIdentity fromExternal(int uid, String packageName) { 63 return new LocalCallingIdentity(-1, uid, packageName); 64 } 65 fromSelf()66 public static LocalCallingIdentity fromSelf() { 67 final LocalCallingIdentity ident = new LocalCallingIdentity( 68 android.os.Process.myPid(), 69 android.os.Process.myUid(), 70 AppGlobals.getInitialApplication().getOpPackageName()); 71 72 ident.packageName = ident.packageNameUnchecked; 73 ident.packageNameResolved = true; 74 ident.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; 75 ident.targetSdkVersionResolved = true; 76 ident.hasPermission = ~(PERMISSION_IS_LEGACY | PERMISSION_IS_REDACTION_NEEDED); 77 ident.hasPermissionResolved = ~0; 78 79 return ident; 80 } 81 getContext()82 private Context getContext() { 83 return AppGlobals.getInitialApplication(); 84 } 85 86 private String packageName; 87 private boolean packageNameResolved; 88 getPackageName()89 public String getPackageName() { 90 if (!packageNameResolved) { 91 packageName = getPackageNameInternal(); 92 packageNameResolved = true; 93 } 94 return packageName; 95 } 96 getPackageNameInternal()97 private String getPackageNameInternal() { 98 // Verify that package name is actually owned by UID 99 getContext().getSystemService(AppOpsManager.class) 100 .checkPackage(uid, packageNameUnchecked); 101 return packageNameUnchecked; 102 } 103 104 private String[] sharedPackageNames; 105 private boolean sharedPackageNamesResolved; 106 getSharedPackageNames()107 public String[] getSharedPackageNames() { 108 if (!sharedPackageNamesResolved) { 109 sharedPackageNames = getSharedPackageNamesInternal(); 110 sharedPackageNamesResolved = true; 111 } 112 return sharedPackageNames; 113 } 114 getSharedPackageNamesInternal()115 private String[] getSharedPackageNamesInternal() { 116 return ArrayUtils.defeatNullable(getContext().getPackageManager().getPackagesForUid(uid)); 117 } 118 119 private int targetSdkVersion; 120 private boolean targetSdkVersionResolved; 121 getTargetSdkVersion()122 public int getTargetSdkVersion() { 123 if (!targetSdkVersionResolved) { 124 targetSdkVersion = getTargetSdkVersionInternal(); 125 targetSdkVersionResolved = true; 126 } 127 return targetSdkVersion; 128 } 129 getTargetSdkVersionInternal()130 private int getTargetSdkVersionInternal() { 131 try { 132 final ApplicationInfo ai = getContext().getPackageManager() 133 .getApplicationInfo(getPackageName(), 0); 134 if (ai != null) { 135 return ai.targetSdkVersion; 136 } 137 } catch (NameNotFoundException ignored) { 138 } 139 return Build.VERSION_CODES.CUR_DEVELOPMENT; 140 } 141 142 public static final int PERMISSION_IS_SYSTEM = 1 << 0; 143 public static final int PERMISSION_IS_LEGACY = 1 << 1; 144 public static final int PERMISSION_IS_REDACTION_NEEDED = 1 << 2; 145 public static final int PERMISSION_READ_AUDIO = 1 << 3; 146 public static final int PERMISSION_READ_VIDEO = 1 << 4; 147 public static final int PERMISSION_READ_IMAGES = 1 << 5; 148 public static final int PERMISSION_WRITE_AUDIO = 1 << 6; 149 public static final int PERMISSION_WRITE_VIDEO = 1 << 7; 150 public static final int PERMISSION_WRITE_IMAGES = 1 << 8; 151 152 private int hasPermission; 153 private int hasPermissionResolved; 154 hasPermission(int permission)155 public boolean hasPermission(int permission) { 156 if ((hasPermissionResolved & permission) == 0) { 157 if (hasPermissionInternal(permission)) { 158 hasPermission |= permission; 159 } 160 hasPermissionResolved |= permission; 161 } 162 return (hasPermission & permission) != 0; 163 } 164 hasPermissionInternal(int permission)165 private boolean hasPermissionInternal(int permission) { 166 switch (permission) { 167 case PERMISSION_IS_SYSTEM: 168 return isSystemInternal(); 169 case PERMISSION_IS_LEGACY: 170 return isLegacyInternal(); 171 case PERMISSION_IS_REDACTION_NEEDED: 172 return isRedactionNeededInternal(); 173 case PERMISSION_READ_AUDIO: 174 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 175 .checkPermissionReadAudio(false, pid, uid, getPackageName()); 176 case PERMISSION_READ_VIDEO: 177 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 178 .checkPermissionReadVideo(false, pid, uid, getPackageName()); 179 case PERMISSION_READ_IMAGES: 180 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 181 .checkPermissionReadImages(false, pid, uid, getPackageName()); 182 case PERMISSION_WRITE_AUDIO: 183 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 184 .checkPermissionWriteAudio(false, pid, uid, getPackageName()); 185 case PERMISSION_WRITE_VIDEO: 186 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 187 .checkPermissionWriteVideo(false, pid, uid, getPackageName()); 188 case PERMISSION_WRITE_IMAGES: 189 return AppGlobals.getInitialApplication().getSystemService(StorageManager.class) 190 .checkPermissionWriteImages(false, pid, uid, getPackageName()); 191 default: 192 return false; 193 } 194 } 195 isSystemInternal()196 private boolean isSystemInternal() { 197 if (uid == android.os.Process.SYSTEM_UID) { 198 return true; 199 } 200 201 // Special case to speed up when MediaProvider is calling itself; we 202 // know it always has system permissions 203 if (uid == android.os.Process.myUid()) { 204 return true; 205 } 206 207 // Determine if caller is holding runtime permission 208 final boolean hasStorage = StorageManager.checkPermissionAndAppOp(getContext(), false, 0, 209 uid, getPackageName(), WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE); 210 211 // We're only willing to give out broad access if they also hold 212 // runtime permission; this is a firm CDD requirement 213 final boolean hasFull = getContext() 214 .checkPermission(WRITE_MEDIA_STORAGE, pid, uid) == PERMISSION_GRANTED; 215 216 return hasFull && hasStorage; 217 } 218 isLegacyInternal()219 private boolean isLegacyInternal() { 220 // TODO: keep this logic in sync with StorageManagerService 221 final boolean hasStorage = StorageManager.checkPermissionAndAppOp(getContext(), false, 0, 222 uid, getPackageName(), WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE); 223 final boolean hasLegacy = getContext().getSystemService(AppOpsManager.class) 224 .checkOp(OP_LEGACY_STORAGE, uid, getPackageName()) == MODE_ALLOWED; 225 return (hasLegacy && hasStorage); 226 } 227 isRedactionNeededInternal()228 private boolean isRedactionNeededInternal() { 229 // System internals or callers holding permission have no redaction 230 if (hasPermission(PERMISSION_IS_SYSTEM) || PermissionChecker.checkPermissionForDataDelivery(getContext(), 231 ACCESS_MEDIA_LOCATION, pid, uid, getPackageName()) 232 == PermissionChecker.PERMISSION_GRANTED) { 233 return false; 234 } 235 return true; 236 } 237 238 private long[] ownedIds = EmptyArray.LONG; 239 isOwned(long id)240 public boolean isOwned(long id) { 241 return ArrayUtils.contains(ownedIds, id); 242 } 243 setOwned(long id, boolean owned)244 public void setOwned(long id, boolean owned) { 245 if (owned) { 246 ownedIds = ArrayUtils.appendLong(ownedIds, id); 247 } else { 248 ownedIds = ArrayUtils.removeLong(ownedIds, id); 249 } 250 } 251 } 252