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