diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 22 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 5 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 10 | ||||
-rw-r--r-- | core/java/android/content/SyncManager.java | 12 | ||||
-rw-r--r-- | core/java/android/content/SyncStorageEngine.java | 19 | ||||
-rw-r--r-- | core/java/android/os/Power.java | 7 | ||||
-rw-r--r-- | core/java/com/android/internal/app/ShutdownThread.java | 241 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 8 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 6 |
9 files changed, 322 insertions, 8 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 53e6f34..541f413 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -990,6 +990,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case SHUTDOWN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + boolean res = shutdown(data.readInt()); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + case PEEK_SERVICE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Intent service = Intent.CREATOR.createFromParcel(data); @@ -2160,5 +2168,19 @@ class ActivityManagerProxy implements IActivityManager return res; } + public boolean shutdown(int timeout) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(timeout); + mRemote.transact(SHUTDOWN_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + reply.recycle(); + data.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 2ac6160..56b29c1 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -16,7 +16,6 @@ package android.app; -import android.app.ActivityManager.MemoryInfo; import android.content.ComponentName; import android.content.ContentProviderNative; import android.content.IContentProvider; @@ -34,7 +33,6 @@ import android.os.IInterface; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelFileDescriptor; -import android.text.TextUtils; import android.os.Bundle; import java.util.List; @@ -225,6 +223,8 @@ public interface IActivityManager extends IInterface { public boolean profileControl(String process, boolean start, String path) throws RemoteException; + public boolean shutdown(int timeout) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -370,4 +370,5 @@ public interface IActivityManager extends IInterface { int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85; + int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ebdd588..e8ca27b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -510,6 +510,7 @@ import java.util.Set; * <li> {@link #ACTION_BATTERY_CHANGED} * <li> {@link #ACTION_POWER_CONNECTED} * <li> {@link #ACTION_POWER_DISCONNECTED} + * <li> {@link #ACTION_SHUTDOWN} * </ul> * * <h3>Standard Categories</h3> @@ -1271,6 +1272,15 @@ public class Intent implements Parcelable { public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.POWER_DISCONNECTED"; /** + * Broadcast Action: Device is shutting down. + * This is broadcast when the device is being shut down (completely turned + * off, not sleeping). Once the broadcast is complete, the final shutdown + * will proceed and all unsaved data lost. Apps will not normally need + * to handle this, since the forground activity will be paused as well. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; + /** * Broadcast Action: Indicates low memory condition on the device */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 019abb8..03cfbea 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -255,6 +255,14 @@ class SyncManager implements OnAccountsUpdatedListener { } }; + private BroadcastReceiver mShutdownIntentReceiver = + new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + Log.w(TAG, "Writing sync state before shutdown..."); + getSyncStorageEngine().writeAllState(); + } + }; + private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM"; private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM"; private final SyncHandler mSyncHandler; @@ -301,6 +309,10 @@ class SyncManager implements OnAccountsUpdatedListener { intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); context.registerReceiver(mStorageIntentReceiver, intentFilter); + intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); + intentFilter.setPriority(100); + context.registerReceiver(mShutdownIntentReceiver, intentFilter); + if (!factoryTest) { mNotificationMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index e20e70f..8379795 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -740,7 +740,7 @@ public class SyncStorageEngine extends Handler { } boolean writeStatisticsNow = false; - int day = getCurrentDay(); + int day = getCurrentDayLocked(); if (mDayStats[0] == null) { mDayStats[0] = new DayStats(day); } else if (day != mDayStats[0].day) { @@ -929,7 +929,7 @@ public class SyncStorageEngine extends Handler { } } - private int getCurrentDay() { + private int getCurrentDayLocked() { mCal.setTimeInMillis(System.currentTimeMillis()); final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR); if (mYear != mCal.get(Calendar.YEAR)) { @@ -1009,6 +1009,21 @@ public class SyncStorageEngine extends Handler { return status; } + public void writeAllState() { + synchronized (mAuthorities) { + // Account info is always written so no need to do it here. + + if (mNumPendingFinished > 0) { + // Only write these if they are out of date. + writePendingOperationsLocked(); + } + + // Just always write these... they are likely out of date. + writeStatusLocked(); + writeStatisticsLocked(); + } + } + /** * Read all account information back in to the initial engine state. */ diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java index 47497e5..3679e47 100644 --- a/core/java/android/os/Power.java +++ b/core/java/android/os/Power.java @@ -80,10 +80,9 @@ public class Power public static native int setLastUserActivityTimeout(long ms); /** - * Turn the device off. - * - * This method is considered deprecated in favor of - * {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}. + * Low-level function turn the device off immediately, without trying + * to be clean. Most people should use + * {@link android.internal.app.ShutdownThread} for a clean shutdown. * * @deprecated * @hide diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java new file mode 100644 index 0000000..77d6e20 --- /dev/null +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.internal.app; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.ProgressDialog; +import android.app.AlertDialog; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Handler; +import android.os.RemoteException; +import android.os.Power; +import android.os.ServiceManager; +import android.os.SystemClock; +import com.android.internal.telephony.ITelephony; +import android.util.Log; +import android.view.WindowManager; + +public final class ShutdownThread extends Thread { + // constants + private static final String TAG = "ShutdownThread"; + private static final int MAX_NUM_PHONE_STATE_READS = 16; + private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500; + // maximum time we wait for the shutdown broadcast before going on. + private static final int MAX_BROADCAST_TIME = 10*1000; + + // state tracking + private static Object sIsStartedGuard = new Object(); + private static boolean sIsStarted = false; + + // static instance of this thread + private static final ShutdownThread sInstance = new ShutdownThread(); + + private final Object mBroadcastDoneSync = new Object(); + private boolean mBroadcastDone; + private Context mContext; + private Handler mHandler; + + private ShutdownThread() { + } + + /** + * Request a clean shutdown, waiting for subsystems to clean up their + * state etc. Must be called from a Looper thread in which its UI + * is shown. + * + * @param context Context used to display the shutdown progress dialog. + */ + public static void shutdown(final Context context, boolean confirm) { + // ensure that only one thread is trying to power down. + // any additional calls are just returned + synchronized (sIsStartedGuard){ + if (sIsStarted) { + Log.d(TAG, "Request to shutdown already running, returning."); + return; + } + } + + Log.d(TAG, "Notifying thread to start radio shutdown"); + + if (confirm) { + final AlertDialog dialog = new AlertDialog.Builder(context) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(com.android.internal.R.string.power_off) + .setMessage(com.android.internal.R.string.shutdown_confirm) + .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + beginShutdownSequence(context); + } + }) + .setNegativeButton(com.android.internal.R.string.no, null) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + dialog.show(); + } else { + beginShutdownSequence(context); + } + } + + private static void beginShutdownSequence(Context context) { + synchronized (sIsStartedGuard) { + sIsStarted = true; + } + + // throw up an indeterminate system dialog to indicate radio is + // shutting down. + ProgressDialog pd = new ProgressDialog(context); + pd.setTitle(context.getText(com.android.internal.R.string.power_off)); + pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); + pd.setIndeterminate(true); + pd.setCancelable(false); + pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + + pd.show(); + + // start the thread that initiates shutdown + sInstance.mContext = context; + sInstance.mHandler = new Handler() { + }; + sInstance.start(); + } + + void broadcastDone() { + synchronized (mBroadcastDoneSync) { + mBroadcastDone = true; + mBroadcastDoneSync.notifyAll(); + } + } + + /** + * Makes sure we handle the shutdown gracefully. + * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. + */ + public void run() { + boolean bluetoothOff; + boolean radioOff; + + BroadcastReceiver br = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + // We don't allow apps to cancel this, so ignore the result. + broadcastDone(); + } + }; + + Log.i(TAG, "Sending shutdown broadcast..."); + + // First send the high-level shut down broadcast. + mBroadcastDone = false; + mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null, + br, mHandler, 0, null, null); + + final long endTime = System.currentTimeMillis() + MAX_BROADCAST_TIME; + synchronized (mBroadcastDoneSync) { + while (!mBroadcastDone) { + long delay = endTime - System.currentTimeMillis(); + if (delay <= 0) { + Log.w(TAG, "Shutdown broadcast timed out"); + break; + } + try { + mBroadcastDoneSync.wait(delay); + } catch (InterruptedException e) { + } + } + } + + Log.i(TAG, "Shutting down activity manager..."); + + final IActivityManager am = + ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); + if (am != null) { + try { + am.shutdown(MAX_BROADCAST_TIME); + } catch (RemoteException e) { + } + } + + final ITelephony phone = + ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); + final IBluetoothDevice bluetooth = + IBluetoothDevice.Stub.asInterface(ServiceManager.checkService( + Context.BLUETOOTH_SERVICE)); + + try { + bluetoothOff = bluetooth == null || + bluetooth.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_OFF; + if (!bluetoothOff) { + Log.w(TAG, "Disabling Bluetooth..."); + bluetooth.disable(false); // disable but don't persist new state + } + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during bluetooth shutdown", ex); + bluetoothOff = true; + } + + try { + radioOff = phone == null || !phone.isRadioOn(); + if (!radioOff) { + Log.w(TAG, "Turning off radio..."); + phone.setRadio(false); + } + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during radio shutdown", ex); + radioOff = true; + } + + Log.i(TAG, "Waiting for Bluetooth and Radio..."); + + // Wait a max of 32 seconds for clean shutdown + for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) { + if (!bluetoothOff) { + try { + bluetoothOff = + bluetooth.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_OFF; + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during bluetooth shutdown", ex); + bluetoothOff = true; + } + } + if (!radioOff) { + try { + radioOff = !phone.isRadioOn(); + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during radio shutdown", ex); + radioOff = true; + } + } + if (radioOff && bluetoothOff) { + Log.i(TAG, "Radio and Bluetooth shutdown complete."); + break; + } + SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC); + } + + //shutdown power + Log.i(TAG, "Performing low-level shutdown..."); + Power.shutdown(); + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 02a6e92..9dd2058 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -803,6 +803,14 @@ android:description="@string/permdesc_runSetActivityWatcher" android:protectionLevel="signature" /> + <!-- Allows an application to watch and control how activities are + started globally in the system. Only for is in debugging + (usually the monkey command). --> + <permission android:name="android.permission.SHUTDOWN" + android:label="@string/permlab_shutdown" + android:description="@string/permdesc_shutdown" + android:protectionLevel="signature" /> + <!-- Allows an application to retrieve the current state of keys and switches. This is only for use by the system.--> <permission android:name="android.permission.READ_INPUT_STATE" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1f20237..d016689 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -474,6 +474,12 @@ the system, and steal or corrupt any data on it.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_shutdown">partial shutdown</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_shutdown">Puts the activity manager into a shutdown + state. Does not perform a complete shutdown.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_runSetActivityWatcher">monitor and control all application launching</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_runSetActivityWatcher">Allows an application to |