1 /* 2 * Copyright (C) 2007 Google Inc. 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.systemui.usb; 18 19 import com.android.internal.R; 20 import android.app.Activity; 21 import android.app.ActivityManager; 22 import android.app.AlertDialog; 23 import android.app.Dialog; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.DialogInterface.OnCancelListener; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.PackageManager.NameNotFoundException; 33 import android.hardware.Usb; 34 import android.os.Bundle; 35 import android.os.Environment; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.IBinder; 39 import android.os.storage.IMountService; 40 import android.os.storage.StorageManager; 41 import android.os.storage.StorageEventListener; 42 import android.os.RemoteException; 43 import android.os.ServiceManager; 44 import android.widget.ImageView; 45 import android.widget.Button; 46 import android.widget.ProgressBar; 47 import android.widget.TextView; 48 import android.view.View; 49 import android.view.Window; 50 import android.view.WindowManager; 51 import android.util.Log; 52 53 import java.util.List; 54 55 /** 56 * This activity is shown to the user for him/her to enable USB mass storage 57 * on-demand (that is, when the USB cable is connected). It uses the alert 58 * dialog style. It will be launched from a notification. 59 */ 60 public class UsbStorageActivity extends Activity 61 implements View.OnClickListener, OnCancelListener { 62 private static final String TAG = "UsbStorageActivity"; 63 64 private Button mMountButton; 65 private Button mUnmountButton; 66 private ProgressBar mProgressBar; 67 private TextView mBanner; 68 private TextView mMessage; 69 private ImageView mIcon; 70 private StorageManager mStorageManager = null; 71 private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1; 72 private static final int DLG_ERROR_SHARING = 2; 73 static final boolean localLOGV = false; 74 75 // UI thread 76 private Handler mUIHandler; 77 78 // thread for working with the storage services, which can be slow 79 private Handler mAsyncStorageHandler; 80 81 /** Used to detect when the USB cable is unplugged, so we can call finish() */ 82 private BroadcastReceiver mUsbStateReceiver = new BroadcastReceiver() { 83 @Override 84 public void onReceive(Context context, Intent intent) { 85 if (intent.getAction().equals(Usb.ACTION_USB_STATE)) { 86 handleUsbStateChanged(intent); 87 } 88 } 89 }; 90 91 private StorageEventListener mStorageListener = new StorageEventListener() { 92 @Override 93 public void onStorageStateChanged(String path, String oldState, String newState) { 94 final boolean on = newState.equals(Environment.MEDIA_SHARED); 95 switchDisplay(on); 96 } 97 }; 98 99 @Override 100 protected void onCreate(Bundle savedInstanceState) { 101 super.onCreate(savedInstanceState); 102 103 if (mStorageManager == null) { 104 mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); 105 if (mStorageManager == null) { 106 Log.w(TAG, "Failed to get StorageManager"); 107 } 108 } 109 110 mUIHandler = new Handler(); 111 112 HandlerThread thr = new HandlerThread("SystemUI UsbStorageActivity"); 113 thr.start(); 114 mAsyncStorageHandler = new Handler(thr.getLooper()); 115 116 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 117 setProgressBarIndeterminateVisibility(true); 118 119 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); 120 if (Environment.isExternalStorageRemovable()) { 121 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 122 } 123 124 setTitle(getString(com.android.internal.R.string.usb_storage_activity_title)); 125 126 setContentView(com.android.internal.R.layout.usb_storage_activity); 127 128 mIcon = (ImageView) findViewById(com.android.internal.R.id.icon); 129 mBanner = (TextView) findViewById(com.android.internal.R.id.banner); 130 mMessage = (TextView) findViewById(com.android.internal.R.id.message); 131 132 mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button); 133 mMountButton.setOnClickListener(this); 134 mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button); 135 mUnmountButton.setOnClickListener(this); 136 mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress); 137 } 138 139 private void switchDisplay(final boolean usbStorageInUse) { 140 mUIHandler.post(new Runnable() { 141 @Override 142 public void run() { 143 switchDisplayAsync(usbStorageInUse); 144 } 145 }); 146 } 147 148 private void switchDisplayAsync(boolean usbStorageInUse) { 149 if (usbStorageInUse) { 150 mProgressBar.setVisibility(View.GONE); 151 mUnmountButton.setVisibility(View.VISIBLE); 152 mMountButton.setVisibility(View.GONE); 153 mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected); 154 mBanner.setText(com.android.internal.R.string.usb_storage_stop_title); 155 mMessage.setText(com.android.internal.R.string.usb_storage_stop_message); 156 } else { 157 mProgressBar.setVisibility(View.GONE); 158 mUnmountButton.setVisibility(View.GONE); 159 mMountButton.setVisibility(View.VISIBLE); 160 mIcon.setImageResource(com.android.internal.R.drawable.usb_android); 161 mBanner.setText(com.android.internal.R.string.usb_storage_title); 162 mMessage.setText(com.android.internal.R.string.usb_storage_message); 163 } 164 } 165 166 @Override 167 protected void onResume() { 168 super.onResume(); 169 170 mStorageManager.registerListener(mStorageListener); 171 registerReceiver(mUsbStateReceiver, new IntentFilter(Usb.ACTION_USB_STATE)); 172 try { 173 mAsyncStorageHandler.post(new Runnable() { 174 @Override 175 public void run() { 176 switchDisplay(mStorageManager.isUsbMassStorageEnabled()); 177 } 178 }); 179 } catch (Exception ex) { 180 Log.e(TAG, "Failed to read UMS enable state", ex); 181 } 182 } 183 184 @Override 185 protected void onPause() { 186 super.onPause(); 187 188 unregisterReceiver(mUsbStateReceiver); 189 if (mStorageManager == null && mStorageListener != null) { 190 mStorageManager.unregisterListener(mStorageListener); 191 } 192 } 193 194 private void handleUsbStateChanged(Intent intent) { 195 boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED); 196 if (!connected) { 197 // It was disconnected from the plug, so finish 198 finish(); 199 } 200 } 201 202 private IMountService getMountService() { 203 IBinder service = ServiceManager.getService("mount"); 204 if (service != null) { 205 return IMountService.Stub.asInterface(service); 206 } 207 return null; 208 } 209 210 @Override 211 public Dialog onCreateDialog(int id, Bundle args) { 212 switch (id) { 213 case DLG_CONFIRM_KILL_STORAGE_USERS: 214 return new AlertDialog.Builder(this) 215 .setTitle(R.string.dlg_confirm_kill_storage_users_title) 216 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 217 public void onClick(DialogInterface dialog, int which) { 218 switchUsbMassStorage(true); 219 }}) 220 .setNegativeButton(R.string.cancel, null) 221 .setMessage(R.string.dlg_confirm_kill_storage_users_text) 222 .setOnCancelListener(this) 223 .create(); 224 case DLG_ERROR_SHARING: 225 return new AlertDialog.Builder(this) 226 .setTitle(R.string.dlg_error_title) 227 .setNeutralButton(R.string.dlg_ok, null) 228 .setMessage(R.string.usb_storage_error_message) 229 .setOnCancelListener(this) 230 .create(); 231 } 232 return null; 233 } 234 235 private void showDialogInner(int id) { 236 removeDialog(id); 237 showDialog(id); 238 } 239 240 private void switchUsbMassStorage(final boolean on) { 241 // things to do on the UI thread 242 mUIHandler.post(new Runnable() { 243 @Override 244 public void run() { 245 mUnmountButton.setVisibility(View.GONE); 246 mMountButton.setVisibility(View.GONE); 247 248 mProgressBar.setVisibility(View.VISIBLE); 249 // will be hidden once USB mass storage kicks in (or fails) 250 } 251 }); 252 253 // things to do elsewhere 254 mAsyncStorageHandler.post(new Runnable() { 255 @Override 256 public void run() { 257 if (on) { 258 mStorageManager.enableUsbMassStorage(); 259 } else { 260 mStorageManager.disableUsbMassStorage(); 261 } 262 } 263 }); 264 } 265 266 private void checkStorageUsers() { 267 mAsyncStorageHandler.post(new Runnable() { 268 @Override 269 public void run() { 270 checkStorageUsersAsync(); 271 } 272 }); 273 } 274 275 private void checkStorageUsersAsync() { 276 IMountService ims = getMountService(); 277 if (ims == null) { 278 // Display error dialog 279 showDialogInner(DLG_ERROR_SHARING); 280 } 281 String extStoragePath = Environment.getExternalStorageDirectory().toString(); 282 boolean showDialog = false; 283 try { 284 int[] stUsers = ims.getStorageUsers(extStoragePath); 285 if (stUsers != null && stUsers.length > 0) { 286 showDialog = true; 287 } else { 288 // List of applications on sdcard. 289 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); 290 List<ApplicationInfo> infoList = am.getRunningExternalApplications(); 291 if (infoList != null && infoList.size() > 0) { 292 showDialog = true; 293 } 294 } 295 } catch (RemoteException e) { 296 // Display error dialog 297 showDialogInner(DLG_ERROR_SHARING); 298 } 299 if (showDialog) { 300 // Display dialog to user 301 showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS); 302 } else { 303 if (localLOGV) Log.i(TAG, "Enabling UMS"); 304 switchUsbMassStorage(true); 305 } 306 } 307 308 public void onClick(View v) { 309 if (v == mMountButton) { 310 // Check for list of storage users and display dialog if needed. 311 checkStorageUsers(); 312 } else if (v == mUnmountButton) { 313 if (localLOGV) Log.i(TAG, "Disabling UMS"); 314 switchUsbMassStorage(false); 315 } 316 } 317 318 public void onCancel(DialogInterface dialog) { 319 finish(); 320 } 321 322 } 323