Home | History | Annotate | Download | only in usb
      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 android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.app.AlertDialog;
     22 import android.app.Dialog;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.DialogInterface.OnCancelListener;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.ApplicationInfo;
     30 import android.hardware.usb.UsbManager;
     31 import android.os.Bundle;
     32 import android.os.Environment;
     33 import android.os.Handler;
     34 import android.os.HandlerThread;
     35 import android.os.IBinder;
     36 import android.os.RemoteException;
     37 import android.os.ServiceManager;
     38 import android.os.storage.IMountService;
     39 import android.os.storage.StorageEventListener;
     40 import android.os.storage.StorageManager;
     41 import android.util.Log;
     42 import android.view.View;
     43 import android.view.WindowManager;
     44 import android.widget.Button;
     45 import android.widget.ImageView;
     46 import android.widget.ProgressBar;
     47 import android.widget.TextView;
     48 
     49 import com.android.internal.R;
     50 
     51 import java.util.List;
     52 
     53 /**
     54  * This activity is shown to the user for him/her to enable USB mass storage
     55  * on-demand (that is, when the USB cable is connected). It uses the alert
     56  * dialog style. It will be launched from a notification.
     57  */
     58 public class UsbStorageActivity extends Activity
     59         implements View.OnClickListener, OnCancelListener {
     60     private static final String TAG = "UsbStorageActivity";
     61 
     62     private Button mMountButton;
     63     private Button mUnmountButton;
     64     private ProgressBar mProgressBar;
     65     private TextView mBanner;
     66     private TextView mMessage;
     67     private ImageView mIcon;
     68     private StorageManager mStorageManager = null;
     69     private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1;
     70     private static final int DLG_ERROR_SHARING = 2;
     71     static final boolean localLOGV = false;
     72     private boolean mDestroyed;
     73 
     74     // UI thread
     75     private Handler mUIHandler;
     76 
     77     // thread for working with the storage services, which can be slow
     78     private Handler mAsyncStorageHandler;
     79 
     80     /** Used to detect when the USB cable is unplugged, so we can call finish() */
     81     private BroadcastReceiver mUsbStateReceiver = new BroadcastReceiver() {
     82         @Override
     83         public void onReceive(Context context, Intent intent) {
     84             if (intent.getAction().equals(UsbManager.ACTION_USB_STATE)) {
     85                 handleUsbStateChanged(intent);
     86             }
     87         }
     88     };
     89 
     90     private StorageEventListener mStorageListener = new StorageEventListener() {
     91         @Override
     92         public void onStorageStateChanged(String path, String oldState, String newState) {
     93             final boolean on = newState.equals(Environment.MEDIA_SHARED);
     94             switchDisplay(on);
     95         }
     96     };
     97 
     98     @Override
     99     protected void onCreate(Bundle savedInstanceState) {
    100         super.onCreate(savedInstanceState);
    101 
    102         if (mStorageManager == null) {
    103             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
    104             if (mStorageManager == null) {
    105                 Log.w(TAG, "Failed to get StorageManager");
    106             }
    107         }
    108 
    109         mUIHandler = new Handler();
    110 
    111         HandlerThread thr = new HandlerThread("SystemUI UsbStorageActivity");
    112         thr.start();
    113         mAsyncStorageHandler = new Handler(thr.getLooper());
    114 
    115         getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
    116         if (Environment.isExternalStorageRemovable()) {
    117             getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    118         }
    119 
    120         setContentView(com.android.internal.R.layout.usb_storage_activity);
    121 
    122         mIcon = (ImageView) findViewById(com.android.internal.R.id.icon);
    123         mBanner = (TextView) findViewById(com.android.internal.R.id.banner);
    124         mMessage = (TextView) findViewById(com.android.internal.R.id.message);
    125 
    126         mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button);
    127         mMountButton.setOnClickListener(this);
    128         mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
    129         mUnmountButton.setOnClickListener(this);
    130         mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress);
    131     }
    132 
    133     @Override
    134     protected void onDestroy() {
    135         super.onDestroy();
    136         mDestroyed = true;
    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(UsbManager.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(UsbManager.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 scheduleShowDialog(final int id) {
    236         mUIHandler.post(new Runnable() {
    237             @Override
    238             public void run() {
    239                 if (!mDestroyed) {
    240                     removeDialog(id);
    241                     showDialog(id);
    242                 }
    243             }
    244         });
    245     }
    246 
    247     private void switchUsbMassStorage(final boolean on) {
    248         // things to do on the UI thread
    249         mUIHandler.post(new Runnable() {
    250             @Override
    251             public void run() {
    252                 mUnmountButton.setVisibility(View.GONE);
    253                 mMountButton.setVisibility(View.GONE);
    254 
    255                 mProgressBar.setVisibility(View.VISIBLE);
    256                 // will be hidden once USB mass storage kicks in (or fails)
    257             }
    258         });
    259 
    260         // things to do elsewhere
    261         mAsyncStorageHandler.post(new Runnable() {
    262             @Override
    263             public void run() {
    264                 if (on) {
    265                     mStorageManager.enableUsbMassStorage();
    266                 } else {
    267                     mStorageManager.disableUsbMassStorage();
    268                 }
    269             }
    270         });
    271     }
    272 
    273     private void checkStorageUsers() {
    274         mAsyncStorageHandler.post(new Runnable() {
    275             @Override
    276             public void run() {
    277                 checkStorageUsersAsync();
    278             }
    279         });
    280     }
    281 
    282     private void checkStorageUsersAsync() {
    283         IMountService ims = getMountService();
    284         if (ims == null) {
    285             // Display error dialog
    286             scheduleShowDialog(DLG_ERROR_SHARING);
    287         }
    288         String extStoragePath = Environment.getExternalStorageDirectory().toString();
    289         boolean showDialog = false;
    290         try {
    291             int[] stUsers = ims.getStorageUsers(extStoragePath);
    292             if (stUsers != null && stUsers.length > 0) {
    293                 showDialog = true;
    294             } else {
    295                 // List of applications on sdcard.
    296                 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    297                 List<ApplicationInfo> infoList = am.getRunningExternalApplications();
    298                 if (infoList != null && infoList.size() > 0) {
    299                     showDialog = true;
    300                 }
    301             }
    302         } catch (RemoteException e) {
    303             // Display error dialog
    304             scheduleShowDialog(DLG_ERROR_SHARING);
    305         }
    306         if (showDialog) {
    307             // Display dialog to user
    308             scheduleShowDialog(DLG_CONFIRM_KILL_STORAGE_USERS);
    309         } else {
    310             if (localLOGV) Log.i(TAG, "Enabling UMS");
    311             switchUsbMassStorage(true);
    312         }
    313     }
    314 
    315     public void onClick(View v) {
    316         if (v == mMountButton) {
    317            // Check for list of storage users and display dialog if needed.
    318             checkStorageUsers();
    319         } else if (v == mUnmountButton) {
    320             if (localLOGV) Log.i(TAG, "Disabling UMS");
    321             switchUsbMassStorage(false);
    322         }
    323     }
    324 
    325     public void onCancel(DialogInterface dialog) {
    326         finish();
    327     }
    328 
    329 }
    330