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 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