1/* 2** 3** Copyright 2006, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18package com.android.development; 19 20import com.android.development.R; 21 22import java.util.ArrayList; 23import java.util.HashSet; 24import java.util.List; 25 26import android.app.Activity; 27import android.app.AlertDialog; 28import android.app.Dialog; 29import android.content.Context; 30import android.content.DialogInterface; 31import android.content.Intent; 32import android.content.DialogInterface.OnCancelListener; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.PackageInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PermissionInfo; 37import android.content.pm.PackageManager.NameNotFoundException; 38import android.os.Bundle; 39import android.os.Handler; 40import android.os.Message; 41import android.util.Log; 42import android.view.LayoutInflater; 43import android.view.View; 44import android.view.ViewGroup; 45import android.widget.AdapterView; 46import android.widget.BaseAdapter; 47import android.widget.ImageView; 48import android.widget.LinearLayout; 49import android.widget.ListView; 50import android.widget.TextView; 51import android.widget.AdapterView.OnItemClickListener; 52 53/** 54 * This activity displays permission details including 55 * the list of apps using a permission. 56 */ 57public class PermissionDetails extends Activity implements OnCancelListener, OnItemClickListener { 58 private static final String TAG = "PermissionDetails"; 59 PackageManager mPm; 60 // layout inflater object used to inflate views 61 private LayoutInflater mInflater; 62 private AppListAdapter mAdapter; 63 64 // Dialog related 65 private static final int DLG_BASE = 0; 66 private static final int DLG_ERROR = DLG_BASE + 1; 67 private static final String PROTECTION_NORMAL="Normal"; 68 private static final String PROTECTION_DANGEROUS="Dangerous"; 69 private static final String PROTECTION_SIGNATURE="Signature"; 70 private static final String PROTECTION_SIGNATURE_OR_SYSTEM="SignatureOrSystem"; 71 72 private static final String KEY_APPS_USING_PERM="AppsUsingPerm"; 73 74 private static final int HANDLER_MSG_BASE = 0; 75 private static final int HANDLER_MSG_GET_APPS = HANDLER_MSG_BASE + 1; 76 private Handler mHandler = new Handler() { 77 public void handleMessage(Message msg) { 78 switch (msg.what) { 79 case HANDLER_MSG_GET_APPS: 80 ArrayList<PackageInfo> appList = msg.getData().getParcelableArrayList(KEY_APPS_USING_PERM); 81 createAppList(appList); 82 break; 83 } 84 } 85 }; 86 87 // View Holder used when displaying views 88 static class AppViewHolder { 89 TextView pkgName; 90 } 91 92 class AppListAdapter extends BaseAdapter { 93 private List<PackageInfo> mList; 94 95 AppListAdapter(List<PackageInfo> list) { 96 mList = list; 97 } 98 99 public int getCount() { 100 return mList.size(); 101 } 102 103 public Object getItem(int position) { 104 return mList.get(position); 105 } 106 107 public long getItemId(int position) { 108 return position; 109 } 110 111 public PackageInfo getPkg(int position) { 112 return mList.get(position); 113 } 114 115 public View getView(int position, View convertView, ViewGroup parent) { 116 // A ViewHolder keeps references to children views to avoid unneccessary calls 117 // to findViewById() on each row. 118 AppViewHolder holder; 119 120 // When convertView is not null, we can reuse it directly, there is no need 121 // to reinflate it. We only inflate a new View when the convertView supplied 122 // by ListView is null. 123 if (convertView == null) { 124 convertView = mInflater.inflate(R.layout.pkg_list_item, null); 125 126 // Creates a ViewHolder and store references to the two children views 127 // we want to bind data to. 128 holder = new AppViewHolder(); 129 holder.pkgName = (TextView) convertView.findViewById(R.id.pkg_name); 130 convertView.setTag(holder); 131 } else { 132 // Get the ViewHolder back to get fast access to the TextView 133 // and the ImageView. 134 holder = (AppViewHolder) convertView.getTag(); 135 } 136 // Bind the data efficiently with the holder 137 PackageInfo pInfo = mList.get(position); 138 holder.pkgName.setText(pInfo.packageName); 139 return convertView; 140 } 141 } 142 143 private void createAppList(List<PackageInfo> list) { 144 Log.i(TAG, "list.size=" + list.size()); 145 for (PackageInfo pkg : list) { 146 Log.i(TAG, "Adding pkg : " + pkg.packageName); 147 } 148 ListView listView = (ListView)findViewById(android.R.id.list); 149 mAdapter = new AppListAdapter(list); 150 ListView lv= (ListView) findViewById(android.R.id.list); 151 lv.setOnItemClickListener(this); 152 lv.setSaveEnabled(true); 153 lv.setItemsCanFocus(true); 154 listView.setAdapter(mAdapter); 155 } 156 157 private void getAppsUsingPerm(PermissionInfo pInfo) { 158 List<PackageInfo> list = mPm.getInstalledPackages(PackageManager.GET_PERMISSIONS); 159 HashSet<PackageInfo> set = new HashSet<PackageInfo>(); 160 for (PackageInfo pkg : list) { 161 if (pkg.requestedPermissions == null) { 162 continue; 163 } 164 for (String perm : pkg.requestedPermissions) { 165 if (perm.equalsIgnoreCase(pInfo.name)) { 166 Log.i(TAG, "Pkg:" + pkg.packageName+" uses permission"); 167 set.add(pkg); 168 break; 169 } 170 } 171 } 172 ArrayList<PackageInfo> retList = new ArrayList<PackageInfo>(); 173 for (PackageInfo pkg : set) { 174 retList.add(pkg); 175 } 176 Message msg = mHandler.obtainMessage(HANDLER_MSG_GET_APPS); 177 Bundle data = msg.getData(); 178 data.putParcelableArrayList(KEY_APPS_USING_PERM, retList); 179 mHandler.dispatchMessage(msg); 180 } 181 182 @Override 183 protected void onCreate(Bundle icicle) { 184 super.onCreate(icicle); 185 setContentView(R.layout.permission_details); 186 Intent intent = getIntent(); 187 String permName = intent.getStringExtra("permission"); 188 if(permName == null) { 189 showDialogInner(DLG_ERROR); 190 } 191 mPm = getPackageManager(); 192 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); 193 PermissionInfo pInfo = null; 194 try { 195 pInfo = mPm.getPermissionInfo(permName, 196 PackageManager.GET_PERMISSIONS); 197 } catch (NameNotFoundException e) { 198 showDialogInner(DLG_ERROR); 199 } 200 setTextView(R.id.perm_name, pInfo.name); 201 setTextView(R.id.perm_desc, pInfo.descriptionRes); 202 setTextView(R.id.perm_group, pInfo.group); 203 setProtectionLevel(R.id.perm_protection, pInfo.protectionLevel); 204 setTextView(R.id.perm_source, pInfo.packageName); 205 ApplicationInfo appInfo = null; 206 try { 207 appInfo = mPm.getApplicationInfo(pInfo.packageName, 0); 208 String uidStr = mPm.getNameForUid(appInfo.uid); 209 setTextView(R.id.source_uid, uidStr); 210 } catch (NameNotFoundException e) { 211 } 212 boolean sharedVisibility = false; 213 // List of apps acquiring access via shared user id 214 LinearLayout sharedPanel = (LinearLayout) findViewById(R.id.shared_pkgs_panel); 215 if (appInfo != null) { 216 String[] sharedList = mPm.getPackagesForUid(appInfo.uid); 217 if ((sharedList != null) && (sharedList.length > 1)) { 218 sharedVisibility = true; 219 TextView label = (TextView) sharedPanel.findViewById(R.id.shared_pkgs_label); 220 TextView sharedView = (TextView) sharedPanel.findViewById(R.id.shared_pkgs); 221 label.setVisibility(View.VISIBLE); 222 StringBuilder buff = new StringBuilder(); 223 buff.append(sharedList[0]); 224 for (int i = 1; i < sharedList.length; i++) { 225 buff.append(", "); 226 buff.append(sharedList[i]); 227 } 228 sharedView.setText(buff.toString()); 229 } 230 } 231 if (sharedVisibility) { 232 sharedPanel.setVisibility(View.VISIBLE); 233 } else { 234 sharedPanel.setVisibility(View.GONE); 235 } 236 getAppsUsingPerm(pInfo); 237 } 238 239 private void setProtectionLevel(int viewId, int protectionLevel) { 240 String levelStr = ""; 241 if (protectionLevel == PermissionInfo.PROTECTION_NORMAL) { 242 levelStr = PROTECTION_NORMAL; 243 } else if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { 244 levelStr = PROTECTION_DANGEROUS; 245 } else if (protectionLevel == PermissionInfo.PROTECTION_SIGNATURE) { 246 levelStr = PROTECTION_SIGNATURE; 247 } else if (protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { 248 levelStr = PROTECTION_SIGNATURE_OR_SYSTEM; 249 } else { 250 levelStr = "Invalid"; 251 } 252 setTextView(viewId, levelStr); 253 } 254 255 private void setTextView(int viewId, int textId) { 256 TextView view = (TextView)findViewById(viewId); 257 view.setText(textId); 258 } 259 260 private void setTextView(int viewId, String text) { 261 TextView view = (TextView)findViewById(viewId); 262 view.setText(text); 263 } 264 265 @Override 266 public Dialog onCreateDialog(int id) { 267 if (id == DLG_ERROR) { 268 return new AlertDialog.Builder(this) 269 .setTitle(R.string.dialog_title_error) 270 .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { 271 public void onClick(DialogInterface dialog, int which) { 272 finish(); 273 }}) 274 .setMessage(R.string.invalid_perm_name) 275 .setOnCancelListener(this) 276 .create(); 277 } 278 return null; 279 } 280 281 private void showDialogInner(int id) { 282 // TODO better fix for this? Remove dialog so that it gets created again 283 removeDialog(id); 284 showDialog(id); 285 } 286 287 @Override 288 protected void onResume() { 289 super.onResume(); 290 } 291 292 @Override 293 protected void onStop() { 294 super.onStop(); 295 } 296 297 public void onCancel(DialogInterface dialog) { 298 finish(); 299 } 300 301 public void onItemClick(AdapterView<?> parent, View view, int position, 302 long id) { 303 // TODO Launch app details activity 304 } 305} 306