Add new shutdown observer for MountService.
Use new observer before rebooting and shutting down.
Add some unit tests for unmount and shutdown code paths
Fix registering/unregistering part in MountService
Use ShutdownThread in PowerManager.reboot()
Add reboot support to ShutdownThread.
Remove MountService code from PowerManagerService.java and Power.java.
Clean shutdown/reboot is handled exclusively by ShutdownThread now.
Change-Id: Iefb157451d3d9c426cb431707b870a873c09123d
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 0974f7f..713358d 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
+import android.os.storage.IMountShutdownObserver;
import android.os.storage.StorageResultCode;
import android.os.Handler;
import android.os.Message;
@@ -172,69 +173,110 @@
}
}
+ class ShutdownCallBack extends UnmountCallBack {
+ IMountShutdownObserver observer;
+ ShutdownCallBack(String path, IMountShutdownObserver observer) {
+ super(path, true);
+ this.observer = observer;
+ }
+
+ @Override
+ void handleFinished() {
+ int ret = doUnmountVolume(path, true);
+ if (observer != null) {
+ try {
+ observer.onShutDownComplete(ret);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException when shutting down");
+ }
+ }
+ }
+ }
+
final private Handler mHandler = new Handler() {
ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
+ boolean mRegistered = false;
+
+ void registerReceiver() {
+ mRegistered = true;
+ mContext.registerReceiver(mPmReceiver, mPmFilter);
+ }
+
+ void unregisterReceiver() {
+ mRegistered = false;
+ mContext.unregisterReceiver(mPmReceiver);
+ }
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
- mContext.registerReceiver(mPmReceiver, mPmFilter);
- boolean hasExtPkgs = mPms.updateExternalMediaStatus(false);
- if (!hasExtPkgs) {
- // Unregister right away
- mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+ // Register only if needed.
+ if (!mRegistered) {
+ registerReceiver();
+ boolean hasExtPkgs = mPms.updateExternalMediaStatus(false);
+ if (!hasExtPkgs) {
+ // Unregister right away
+ mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+ }
}
break;
}
case H_UNMOUNT_PM_DONE: {
- // Unregister receiver
- mContext.unregisterReceiver(mPmReceiver);
- UnmountCallBack ucb = mForceUnmounts.get(0);
- if (ucb == null || ucb.path == null) {
- // Just ignore
- return;
+ // Unregister now.
+ if (mRegistered) {
+ unregisterReceiver();
}
- String path = ucb.path;
- boolean done = false;
- if (!ucb.force) {
- done = true;
- } else {
- int pids[] = getStorageUsers(path);
- if (pids == null || pids.length == 0) {
+ int size = mForceUnmounts.size();
+ int sizeArr[] = new int[size];
+ int sizeArrN = 0;
+ for (int i = 0; i < size; i++) {
+ UnmountCallBack ucb = mForceUnmounts.get(i);
+ String path = ucb.path;
+ boolean done = false;
+ if (!ucb.force) {
done = true;
} else {
- // Kill processes holding references first
- ActivityManagerService ams = (ActivityManagerService)
- ServiceManager.getService("activity");
- // Eliminate system process here?
- boolean ret = ams.killPidsForMemory(pids);
- if (ret) {
- // Confirm if file references have been freed.
- pids = getStorageUsers(path);
- if (pids == null || pids.length == 0) {
- done = true;
+ int pids[] = getStorageUsers(path);
+ if (pids == null || pids.length == 0) {
+ done = true;
+ } else {
+ // Kill processes holding references first
+ ActivityManagerService ams = (ActivityManagerService)
+ ServiceManager.getService("activity");
+ // Eliminate system process here?
+ boolean ret = ams.killPidsForMemory(pids);
+ if (ret) {
+ // Confirm if file references have been freed.
+ pids = getStorageUsers(path);
+ if (pids == null || pids.length == 0) {
+ done = true;
+ }
}
}
}
- }
- if (done) {
- mForceUnmounts.remove(0);
- mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
- ucb));
- } else {
- if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
- Log.i(TAG, "Cannot unmount inspite of " +
- MAX_UNMOUNT_RETRIES + " to unmount media");
- // Send final broadcast indicating failure to unmount.
+ if (done) {
+ sizeArr[sizeArrN++] = i;
+ mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
+ ucb));
} else {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
- ucb.retries++),
- RETRY_UNMOUNT_DELAY);
+ if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
+ Log.i(TAG, "Cannot unmount inspite of " +
+ MAX_UNMOUNT_RETRIES + " to unmount media");
+ // Send final broadcast indicating failure to unmount.
+ } else {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
+ ucb.retries++),
+ RETRY_UNMOUNT_DELAY);
+ }
}
}
+ // Remove already processed elements from list.
+ for (int i = (sizeArrN-1); i >= 0; i--) {
+ mForceUnmounts.remove(sizeArr[i]);
+ }
break;
}
case H_UNMOUNT_MS : {
@@ -826,7 +868,7 @@
}
}
- public void shutdown() {
+ public void shutdown(final IMountShutdownObserver observer) {
validatePermission(android.Manifest.permission.SHUTDOWN);
Log.i(TAG, "Shutting down");
@@ -865,12 +907,9 @@
}
if (state.equals(Environment.MEDIA_MOUNTED)) {
- /*
- * If the media is mounted, then gracefully unmount it.
- */
- if (doUnmountVolume(path, true) != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to unmount media for shutdown");
- }
+ // Post a unmount message.
+ ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
+ mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
}
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 0189c60..fc6bfcd 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.app.ShutdownThread;
import com.android.server.am.BatteryStatsService;
import android.app.ActivityManagerNative;
@@ -41,7 +42,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.storage.IMountService;
import android.os.IPowerManager;
import android.os.LocalPowerManager;
import android.os.Power;
@@ -2202,28 +2202,35 @@
{
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
- /*
- * Manually shutdown the MountService to ensure media is
- * put into a safe state.
- */
- IMountService mSvc = IMountService.Stub.asInterface(
- ServiceManager.getService("mount"));
-
- if (mSvc != null) {
- try {
- mSvc.shutdown();
- } catch (Exception e) {
- Slog.e(TAG, "MountService shutdown failed", e);
+ if (mHandler == null || !ActivityManagerNative.isSystemReady()) {
+ throw new IllegalStateException("Too early to call reboot()");
+ }
+
+ final String finalReason = reason;
+ Runnable runnable = new Runnable() {
+ public void run() {
+ synchronized (this) {
+ ShutdownThread.reboot(mContext, finalReason, false);
+ // if we get here we failed
+ notify();
+ }
+
}
- } else {
- Slog.w(TAG, "MountService unavailable for shutdown");
- }
+ };
- try {
- Power.reboot(reason);
- } catch (IOException e) {
- Slog.e(TAG, "reboot failed", e);
+ mHandler.post(runnable);
+
+ // block until we reboot or fail.
+ // throw an exception if we failed to reboot
+ synchronized (runnable) {
+ try {
+ runnable.wait();
+ } catch (InterruptedException e) {
+ }
}
+
+ // if we get here we failed
+ throw new IllegalStateException("unable to reboot!");
}
/**
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index f97f50a..3f64b25 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -29,6 +29,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.Process;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -676,11 +677,8 @@
*/
void rebootSystem(String reason) {
Slog.i(TAG, "Rebooting system because: " + reason);
- try {
- android.os.Power.reboot(reason);
- } catch (IOException e) {
- Slog.e(TAG, "Reboot failed!", e);
- }
+ PowerManagerService pms = (PowerManagerService) ServiceManager.getService("power");
+ pms.reboot(reason);
}
/**