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
17package com.android.systemui.usb;
18
19import com.android.internal.R;
20import android.app.Activity;
21import android.app.ActivityManager;
22import android.app.AlertDialog;
23import android.app.Dialog;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.DialogInterface.OnCancelListener;
30import android.content.pm.ApplicationInfo;
31import android.content.pm.PackageManager;
32import android.content.pm.PackageManager.NameNotFoundException;
33import android.hardware.usb.UsbManager;
34import android.os.Bundle;
35import android.os.Environment;
36import android.os.Handler;
37import android.os.HandlerThread;
38import android.os.IBinder;
39import android.os.storage.IMountService;
40import android.os.storage.StorageManager;
41import android.os.storage.StorageEventListener;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.widget.ImageView;
45import android.widget.Button;
46import android.widget.ProgressBar;
47import android.widget.TextView;
48import android.view.View;
49import android.view.Window;
50import android.view.WindowManager;
51import android.util.Log;
52
53import 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 */
60public 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