Home | History | Annotate | Download | only in wear
      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
     52     public boolean onCreate() {
     53         return true;
     54     }
     55 
     56     @Override
     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
     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
     75     public Uri insert(Uri uri, ContentValues values) {
     76         throw new UnsupportedOperationException("Insert is not supported.");
     77     }
     78 
     79     @Override
     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
     99     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    100         throw new UnsupportedOperationException("Update is not supported.");
    101     }
    102 
    103     @Override
    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 
    122     public static Uri getUriForPackage(final String packageName) {
    123         return Uri.parse("content://" + AUTHORITY + "/icons/" + packageName + ".icon");
    124     }
    125 
    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)
    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)
    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