1 /* 2 * Copyright (C) 2015 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.packageinstaller.wear; 18 19 import android.annotation.TargetApi; 20 import android.app.ActivityManager; 21 import android.content.ContentProvider; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.database.Cursor; 29 import android.net.Uri; 30 import android.os.Binder; 31 import android.os.Build; 32 import android.os.ParcelFileDescriptor; 33 import android.util.Log; 34 35 import java.io.File; 36 import java.io.FileNotFoundException; 37 import java.util.List; 38 39 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 40 41 public class WearPackageIconProvider extends ContentProvider { 42 private static final String TAG = "WearPackageIconProvider"; 43 public static final String AUTHORITY = "com.google.android.packageinstaller.wear.provider"; 44 45 private static final String REQUIRED_PERMISSION = 46 "com.google.android.permission.INSTALL_WEARABLE_PACKAGES"; 47 48 /** MIME types. */ 49 public static final String ICON_TYPE = "vnd.android.cursor.item/cw_package_icon"; 50 51 @Override onCreate()52 public boolean onCreate() { 53 return true; 54 } 55 56 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)57 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 58 String sortOrder) { 59 throw new UnsupportedOperationException("Query is not supported."); 60 } 61 62 @Override getType(Uri uri)63 public String getType(Uri uri) { 64 if (uri == null) { 65 throw new IllegalArgumentException("URI passed in is null."); 66 } 67 68 if (AUTHORITY.equals(uri.getEncodedAuthority())) { 69 return ICON_TYPE; 70 } 71 return null; 72 } 73 74 @Override insert(Uri uri, ContentValues values)75 public Uri insert(Uri uri, ContentValues values) { 76 throw new UnsupportedOperationException("Insert is not supported."); 77 } 78 79 @Override delete(Uri uri, String selection, String[] selectionArgs)80 public int delete(Uri uri, String selection, String[] selectionArgs) { 81 if (uri == null) { 82 throw new IllegalArgumentException("URI passed in is null."); 83 } 84 85 enforcePermissions(uri); 86 87 if (ICON_TYPE.equals(getType(uri))) { 88 final File file = WearPackageUtil.getIconFile( 89 this.getContext().getApplicationContext(), getPackageNameFromUri(uri)); 90 if (file != null) { 91 file.delete(); 92 } 93 } 94 95 return 0; 96 } 97 98 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)99 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 100 throw new UnsupportedOperationException("Update is not supported."); 101 } 102 103 @Override openFile( Uri uri, @SuppressWarnings("unused") String mode)104 public ParcelFileDescriptor openFile( 105 Uri uri, @SuppressWarnings("unused") String mode) throws FileNotFoundException { 106 if (uri == null) { 107 throw new IllegalArgumentException("URI passed in is null."); 108 } 109 110 enforcePermissions(uri); 111 112 if (ICON_TYPE.equals(getType(uri))) { 113 final File file = WearPackageUtil.getIconFile( 114 this.getContext().getApplicationContext(), getPackageNameFromUri(uri)); 115 if (file != null) { 116 return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 117 } 118 } 119 return null; 120 } 121 getUriForPackage(final String packageName)122 public static Uri getUriForPackage(final String packageName) { 123 return Uri.parse("content://" + AUTHORITY + "/icons/" + packageName + ".icon"); 124 } 125 getPackageNameFromUri(Uri uri)126 private String getPackageNameFromUri(Uri uri) { 127 if (uri == null) { 128 return null; 129 } 130 List<String> pathSegments = uri.getPathSegments(); 131 String packageName = pathSegments.get(pathSegments.size() - 1); 132 133 if (packageName.endsWith(".icon")) { 134 packageName = packageName.substring(0, packageName.lastIndexOf(".")); 135 } 136 return packageName; 137 } 138 139 /** 140 * Make sure the calling app is either a system app or the same app or has the right permission. 141 * @throws SecurityException if the caller has insufficient permissions. 142 */ 143 @TargetApi(Build.VERSION_CODES.BASE_1_1) enforcePermissions(Uri uri)144 private void enforcePermissions(Uri uri) { 145 // Redo some of the permission check in {@link ContentProvider}. Just add an extra check to 146 // allow System process to access this provider. 147 Context context = getContext(); 148 final int pid = Binder.getCallingPid(); 149 final int uid = Binder.getCallingUid(); 150 final int myUid = android.os.Process.myUid(); 151 152 if (uid == myUid || isSystemApp(context, pid)) { 153 return; 154 } 155 156 if (context.checkPermission(REQUIRED_PERMISSION, pid, uid) == PERMISSION_GRANTED) { 157 return; 158 } 159 160 // last chance, check against any uri grants 161 if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) 162 == PERMISSION_GRANTED) { 163 return; 164 } 165 166 throw new SecurityException("Permission Denial: reading " 167 + getClass().getName() + " uri " + uri + " from pid=" + pid 168 + ", uid=" + uid); 169 } 170 171 /** 172 * From the pid of the calling process, figure out whether this is a system app or not. We do 173 * this by checking the application information corresponding to the pid and then checking if 174 * FLAG_SYSTEM is set. 175 */ 176 @TargetApi(Build.VERSION_CODES.CUPCAKE) isSystemApp(Context context, int pid)177 private boolean isSystemApp(Context context, int pid) { 178 // Get the Activity Manager Object 179 ActivityManager aManager = 180 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 181 // Get the list of running Applications 182 List<ActivityManager.RunningAppProcessInfo> rapInfoList = 183 aManager.getRunningAppProcesses(); 184 for (ActivityManager.RunningAppProcessInfo rapInfo : rapInfoList) { 185 if (rapInfo.pid == pid) { 186 try { 187 PackageInfo pkgInfo = context.getPackageManager().getPackageInfo( 188 rapInfo.pkgList[0], 0); 189 if (pkgInfo != null && pkgInfo.applicationInfo != null && 190 (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 191 Log.d(TAG, pid + " is a system app."); 192 return true; 193 } 194 } catch (PackageManager.NameNotFoundException e) { 195 Log.e(TAG, "Could not find package information.", e); 196 return false; 197 } 198 } 199 } 200 return false; 201 } 202 } 203