1 /*
2  * Copyright (C) 2018 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 com.android.providers.media.MediaProvider.TAG;
20 
21 import android.app.Activity;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.res.Resources;
29 import android.database.Cursor;
30 import android.graphics.Bitmap;
31 import android.net.Uri;
32 import android.os.AsyncTask;
33 import android.os.Bundle;
34 import android.provider.MediaStore.MediaColumns;
35 import android.text.TextUtils;
36 import android.util.Log;
37 import android.util.Size;
38 import android.view.WindowManager;
39 import android.widget.FrameLayout;
40 import android.widget.ImageView;
41 import android.widget.ImageView.ScaleType;
42 import android.widget.TextView;
43 
44 import com.android.internal.app.AlertActivity;
45 
46 import java.io.IOException;
47 
48 public class PermissionActivity extends AlertActivity implements DialogInterface.OnClickListener {
49     @Override
onCreate(Bundle savedInstanceState)50     public void onCreate(Bundle savedInstanceState) {
51         super.onCreate(savedInstanceState);
52 
53         final CharSequence label;
54         final int resId;
55         try {
56             label = getCallingLabel();
57             resId = getMessageId();
58         } catch (Exception e) {
59             Log.w(TAG, e);
60             finish();
61             return;
62         }
63 
64         final Resources res = getResources();
65         final FrameLayout view = new FrameLayout(this);
66         final int padding = res.getDimensionPixelSize(com.android.internal.R.dimen.default_gap);
67         view.setPadding(padding, padding, padding, padding);
68         new AsyncTask<Void, Void, Description>() {
69             @Override
70             protected Description doInBackground(Void... params) {
71                 try {
72                     return new Description(PermissionActivity.this, getIntent().getData());
73                 } catch (Exception e) {
74                     Log.w(TAG, e);
75                     finish();
76                     return null;
77                 }
78             }
79 
80             @Override
81             protected void onPostExecute(Description result) {
82                 if (result == null) return;
83 
84                 if (result.thumbnail != null) {
85                     Log.d(TAG, "Found thumbnail " + result.thumbnail.getWidth() + "x"
86                             + result.thumbnail.getHeight());
87 
88                     final ImageView child = new ImageView(PermissionActivity.this);
89                     child.setScaleType(ScaleType.CENTER_INSIDE);
90                     child.setImageBitmap(result.thumbnail);
91                     child.setContentDescription(result.contentDescription);
92                     view.addView(child);
93                 } else {
94                     Log.d(TAG, "Found description " + result.contentDescription);
95 
96                     final TextView child = new TextView(PermissionActivity.this);
97                     child.setText(result.contentDescription);
98                     view.addView(child);
99                 }
100             }
101         }.execute();
102 
103         mAlertParams.mMessage = TextUtils.expandTemplate(getText(resId), label);
104         mAlertParams.mPositiveButtonText = getString(R.string.grant_dialog_button_allow);
105         mAlertParams.mPositiveButtonListener = this;
106         mAlertParams.mNegativeButtonText = getString(R.string.grant_dialog_button_deny);
107         mAlertParams.mNegativeButtonListener = this;
108         mAlertParams.mCancelable = false;
109         mAlertParams.mView = view;
110         setupAlert();
111 
112         getWindow().setCloseOnTouchOutside(false);
113         getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
114         getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
115     }
116 
117     @Override
onClick(DialogInterface dialog, int which)118     public void onClick(DialogInterface dialog, int which) {
119         final Uri uri = getIntent().getData();
120         switch (which) {
121             case BUTTON_POSITIVE:
122                 Log.d(TAG, "User allowed grant for " + uri);
123                 grantUriPermission(getCallingPackage(), uri,
124                         Intent.FLAG_GRANT_READ_URI_PERMISSION |
125                                 Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
126                                 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
127                 setResult(Activity.RESULT_OK);
128                 finish();
129                 break;
130             case BUTTON_NEGATIVE:
131                 Log.d(TAG, "User declined grant for " + uri);
132                 finish();
133                 break;
134         }
135     }
136 
getCallingLabel()137     private CharSequence getCallingLabel() throws NameNotFoundException {
138         final String callingPackage = getCallingPackage();
139         if (TextUtils.isEmpty(callingPackage)) {
140             throw new NameNotFoundException("Missing calling package");
141         }
142 
143         final PackageManager pm = getPackageManager();
144         final CharSequence callingLabel = pm
145                 .getApplicationLabel(pm.getApplicationInfo(callingPackage, 0));
146         if (TextUtils.isEmpty(callingLabel)) {
147             throw new NameNotFoundException("Missing calling package");
148         }
149 
150         return callingLabel;
151     }
152 
getMessageId()153     private int getMessageId() throws NameNotFoundException {
154         final Uri uri = getIntent().getData();
155         final String type = uri.getPathSegments().get(1);
156         switch (type) {
157             case "audio": return R.string.permission_audio;
158             case "video": return R.string.permission_video;
159             case "images": return R.string.permission_images;
160         }
161         throw new NameNotFoundException("Unknown media type " + uri);
162     }
163 
164     private static class Description {
165         public Bitmap thumbnail;
166         public CharSequence contentDescription;
167 
Description(Context context, Uri uri)168         public Description(Context context, Uri uri) {
169             final Resources res = context.getResources();
170             final ContentResolver resolver = context.getContentResolver();
171 
172             final Size size = new Size(res.getDisplayMetrics().widthPixels,
173                     res.getDisplayMetrics().widthPixels);
174             try {
175                 thumbnail = resolver.loadThumbnail(uri, size, null);
176             } catch (IOException e) {
177                 Log.w(TAG, e);
178             }
179             try (Cursor c = resolver.query(uri,
180                     new String[] { MediaColumns.DISPLAY_NAME }, null, null)) {
181                 if (c.moveToFirst()) {
182                     contentDescription = c.getString(0);
183                 }
184             }
185 
186             if (TextUtils.isEmpty(contentDescription)) {
187                 throw new IllegalStateException();
188             }
189         }
190     }
191 }
192