Merge "Fix Tabwidget copy/paste bug."
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index cfc2e75..659d70f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,11 +92,6 @@
return;
}
- if ("mountsd".equals(op)) {
- runMountSd();
- return;
- }
-
if ("uninstall".equals(op)) {
runUninstall();
return;
@@ -646,37 +641,6 @@
}
}
- private void runMountSd() {
- String opt;
- boolean mount = false;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-m")) {
- String mountStr = nextOptionData();
- if (mountStr == null) {
- System.err.println("Error: no value specified for -m");
- showUsage();
- return;
- }
- if ("true".equalsIgnoreCase(mountStr)) {
- mount = true;
- } else if ("false".equalsIgnoreCase(mountStr)) {
- mount = false;
- } else {
- System.err.println("Error: no value specified for -m");
- showUsage();
- return;
- }
- }
- }
-
- try {
- mPm.updateExternalMediaStatus(mount);
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- }
- }
-
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
boolean finished;
boolean result;
@@ -866,7 +830,6 @@
System.err.println(" pm path PACKAGE");
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");
System.err.println(" pm uninstall [-k] PACKAGE");
- System.err.println(" pm mountsd [-m true/false]");
System.err.println(" pm enable PACKAGE_OR_COMPONENT");
System.err.println(" pm disable PACKAGE_OR_COMPONENT");
System.err.println("");
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6d5686a..596ca9d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -980,10 +980,11 @@
return true;
}
- case KILL_PIDS_FOR_MEMORY_TRANSACTION: {
+ case KILL_PIDS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int[] pids = data.createIntArray();
- boolean res = killPidsForMemory(pids);
+ String reason = data.readString();
+ boolean res = killPids(pids, reason);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -2393,12 +2394,13 @@
mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0);
data.recycle();
}
- public boolean killPidsForMemory(int[] pids) throws RemoteException {
+ public boolean killPids(int[] pids, String reason) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeIntArray(pids);
- mRemote.transact(KILL_PIDS_FOR_MEMORY_TRANSACTION, data, reply, 0);
+ data.writeString(reason);
+ mRemote.transact(KILL_PIDS_TRANSACTION, data, reply, 0);
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 14571de..30feae1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -241,7 +241,7 @@
public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;
- public boolean killPidsForMemory(int[] pids) throws RemoteException;
+ public boolean killPids(int[] pids, String reason) throws RemoteException;
public void reportPss(IApplicationThread caller, int pss) throws RemoteException;
@@ -476,7 +476,7 @@
int GET_PROCESSES_IN_ERROR_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+76;
int CLEAR_APP_DATA_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+77;
int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
- int KILL_PIDS_FOR_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
+ int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 399a87d..c638d04 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -309,7 +309,7 @@
* MountService uses this to call into the package manager to update
* status of sdcard.
*/
- boolean updateExternalMediaStatus(boolean mounted);
+ void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
String nextPackageToClean(String lastPackage);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bbf4ca1..0318b6c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -233,7 +233,7 @@
/**
* Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to
* indicate that this package should be installed as forward locked, i.e. only the app itself
- * should have access to it's code and non-resource assets.
+ * should have access to its code and non-resource assets.
* @hide
*/
public static final int INSTALL_FORWARD_LOCK = 0x00000001;
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index 75455ab..4862f80 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -84,7 +84,7 @@
int[] getStorageUsers(String path);
/**
- * Gets the state of an volume via it's mountpoint.
+ * Gets the state of a volume via its mountpoint.
*/
String getVolumeState(String mountPoint);
@@ -146,4 +146,10 @@
* Invokes call back once the shutdown is complete.
*/
void shutdown(IMountShutdownObserver observer);
+
+ /**
+ * Call into MountService by PackageManager to notify that its done
+ * processing the media status update request.
+ */
+ void finishMediaUpdate();
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index dae9187..344b390 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -527,20 +527,12 @@
*/
private int getFileSize(String uri) {
int size = 0;
- Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
- new String[] { OpenableColumns.SIZE },
- null,
- null,
- null);
- if (cursor != null) {
- try {
- if (cursor.moveToNext()) {
- size = cursor.getInt(0);
- }
- } finally {
- cursor.close();
- }
- }
+ try {
+ InputStream stream = mContext.getContentResolver()
+ .openInputStream(Uri.parse(uri));
+ size = stream.available();
+ stream.close();
+ } catch (Exception e) {}
return size;
}
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 2a1f23a..d4ac24a 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -424,7 +424,8 @@
// faster.
if ("text/plain".equals(type) || "application/smil".equals(type)) {
String text = c.getString(PART_COLUMN_TEXT);
- byte [] blob = new EncodedStringValue(text).getTextString();
+ byte [] blob = new EncodedStringValue(text != null ? text : "")
+ .getTextString();
baos.write(blob, 0, blob.length);
} else {
@@ -858,7 +859,7 @@
} else {
values.put(Mms.SUBJECT, "");
}
-
+
long messageSize = sendReq.getMessageSize();
if (messageSize > 0) {
values.put(Mms.MESSAGE_SIZE, messageSize);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e2caa0..a3c73d8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -732,7 +732,7 @@
@hide -->
<permission android:name="android.permission.ASEC_ACCESS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_access"
android:description="@string/permdesc_asec_access" />
@@ -740,7 +740,7 @@
@hide -->
<permission android:name="android.permission.ASEC_CREATE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_create"
android:description="@string/permdesc_asec_create" />
@@ -748,7 +748,7 @@
@hide -->
<permission android:name="android.permission.ASEC_DESTROY"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_destroy"
android:description="@string/permdesc_asec_destroy" />
@@ -756,7 +756,7 @@
@hide -->
<permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_mount_unmount"
android:description="@string/permdesc_asec_mount_unmount" />
@@ -764,7 +764,7 @@
@hide -->
<permission android:name="android.permission.ASEC_RENAME"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_rename"
android:description="@string/permdesc_asec_rename" />
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 0a53e9c..81ae26f 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -48,14 +48,15 @@
* instead of periodically.
*/
public class EntropyService extends Binder {
- private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat";
private static final String TAG = "EntropyService";
private static final int ENTROPY_WHAT = 1;
private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
- private static final String RANDOM_DEV = "/dev/urandom";
private static final long START_TIME = System.currentTimeMillis();
private static final long START_NANOTIME = System.nanoTime();
+ private final String randomDevice;
+ private final String entropyFile;
+
/**
* Handler that periodically updates the entropy on disk.
*/
@@ -72,6 +73,16 @@
};
public EntropyService() {
+ this(getSystemDir() + "/entropy.dat", "/dev/urandom");
+ }
+
+ /** Test only interface, not for public use */
+ public EntropyService(String entropyFile, String randomDevice) {
+ if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
+ if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
+
+ this.randomDevice = randomDevice;
+ this.entropyFile = entropyFile;
loadInitialEntropy();
addDeviceSpecificEntropy();
writeEntropy();
@@ -85,7 +96,7 @@
private void loadInitialEntropy() {
try {
- RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV);
+ RandomBlock.fromFile(entropyFile).toFile(randomDevice);
} catch (IOException e) {
Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
}
@@ -93,7 +104,7 @@
private void writeEntropy() {
try {
- RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME);
+ RandomBlock.fromFile(randomDevice).toFile(entropyFile);
} catch (IOException e) {
Slog.w(TAG, "unable to write entropy", e);
}
@@ -116,7 +127,7 @@
private void addDeviceSpecificEntropy() {
PrintWriter out = null;
try {
- out = new PrintWriter(new FileOutputStream(RANDOM_DEV));
+ out = new PrintWriter(new FileOutputStream(randomDevice));
out.println("Copyright (C) 2009 The Android Open Source Project");
out.println("All Your Randomness Are Belong To Us");
out.println(START_TIME);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d50f591..d3ac026 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -135,17 +135,6 @@
private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
private static final int MAX_UNMOUNT_RETRIES = 4;
- private IntentFilter mPmFilter = new IntentFilter(
- Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- private BroadcastReceiver mPmReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
- }
- }
- };
-
class UnmountCallBack {
String path;
int retries;
@@ -200,49 +189,35 @@
class MountServiceHandler extends Handler {
ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
- boolean mRegistered = false;
+ boolean mUpdatingStatus = false;
MountServiceHandler(Looper l) {
super(l);
}
- void registerReceiver() {
- mRegistered = true;
- if (DEBUG_UNMOUNT) Log.i(TAG, "Registering receiver");
- mContext.registerReceiver(mPmReceiver, mPmFilter);
- }
-
- void unregisterReceiver() {
- mRegistered = false;
- if (DEBUG_UNMOUNT) Log.i(TAG, "Unregistering receiver");
- mContext.unregisterReceiver(mPmReceiver);
- }
-
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_PM_UPDATE");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
- if (DEBUG_UNMOUNT) Log.i(TAG, " registered = " + mRegistered);
+ if (DEBUG_UNMOUNT) Log.i(TAG, " registered = " + mUpdatingStatus);
// Register only if needed.
- if (!mRegistered) {
- registerReceiver();
- if (DEBUG_UNMOUNT) Log.i(TAG, "Updating external media status");
- boolean hasExtPkgs = mPms.updateExternalMediaStatus(false);
- if (!hasExtPkgs) {
- // Unregister right away
- mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
- }
+ if (!mUpdatingStatus) {
+ if (DEBUG_UNMOUNT) Log.i(TAG, "Updating external media status on PackageManager");
+ mUpdatingStatus = true;
+ mPms.updateExternalMediaStatus(false, true);
}
break;
}
case H_UNMOUNT_PM_DONE: {
if (DEBUG_UNMOUNT) Log.i(TAG, "H_UNMOUNT_PM_DONE");
- // Unregister now.
- if (mRegistered) {
- unregisterReceiver();
+ if (!mUpdatingStatus) {
+ // Does not correspond to unmount's status update.
+ return;
}
+ if (DEBUG_UNMOUNT) Log.i(TAG, "Updated status. Processing requests");
+ mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
@@ -261,7 +236,7 @@
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
// Eliminate system process here?
- boolean ret = ams.killPidsForMemory(pids);
+ boolean ret = ams.killPids(pids, "Unmount media");
if (ret) {
// Confirm if file references have been freed.
pids = getStorageUsers(path);
@@ -277,8 +252,8 @@
ucb));
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
- Log.i(TAG, "Cannot unmount inspite of " +
- MAX_UNMOUNT_RETRIES + " to unmount media");
+ Log.i(TAG, "Cannot unmount media inspite of " +
+ MAX_UNMOUNT_RETRIES + " retries");
// Send final broadcast indicating failure to unmount.
} else {
mHandler.sendMessageDelayed(
@@ -337,16 +312,23 @@
public void run() {
try {
String path = Environment.getExternalStorageDirectory().getPath();
- if (getVolumeState(
- Environment.getExternalStorageDirectory().getPath()).equals(
- Environment.MEDIA_UNMOUNTED)) {
+ String state = getVolumeState(path);
+
+ if (state.equals(Environment.MEDIA_UNMOUNTED)) {
int rc = doMountVolume(path);
if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
}
+ } else if (state.equals(Environment.MEDIA_SHARED)) {
+ /*
+ * Bootstrap UMS enabled state since vold indicates
+ * the volume is shared (runtime restart while ums enabled)
+ */
+ notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
}
+
/*
- * If UMS is connected in boot, send the connected event
+ * If UMS was connected on boot, send the connected event
* now that we're up.
*/
if (mSendUmsConnectedOnBoot) {
@@ -405,9 +387,9 @@
}
// Update state on PackageManager
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
- mPms.updateExternalMediaStatus(false);
+ mPms.updateExternalMediaStatus(false, false);
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
- mPms.updateExternalMediaStatus(true);
+ mPms.updateExternalMediaStatus(true, false);
}
String oldState = mLegacyState;
mLegacyState = state;
@@ -750,19 +732,15 @@
if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
return VoldResponseCode.OpFailedVolNotMounted;
}
-
- // We unmounted the volume. No of the asec containers are available now.
- synchronized (mAsecMountSet) {
- mAsecMountSet.clear();
- }
- // Notify PackageManager of potential media removal and deal with
- // return code later on. The caller of this api should be aware or have been
- // notified that the applications installed on the media will be killed.
// Redundant probably. But no harm in updating state again.
- mPms.updateExternalMediaStatus(false);
+ mPms.updateExternalMediaStatus(false, false);
try {
mConnector.doCommand(String.format(
"volume unmount %s%s", path, (force ? " force" : "")));
+ // We unmounted the volume. None of the asec containers are available now.
+ synchronized (mAsecMountSet) {
+ mAsecMountSet.clear();
+ }
return StorageResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
// Don't worry about mismatch in PackageManager since the
@@ -1325,5 +1303,9 @@
Log.e(TAG, "Got an empty response");
return "";
}
+
+ public void finishMediaUpdate() {
+ mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+ }
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 48b3fbb..9eb63a6 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -343,6 +343,7 @@
static final int POST_INSTALL = 9;
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
+ static final int UPDATED_MEDIA_STATUS = 12;
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
@@ -596,6 +597,13 @@
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
} break;
+ case UPDATED_MEDIA_STATUS: {
+ try {
+ PackageHelper.getMountService().finishMediaUpdate();
+ } catch (RemoteException e) {
+ Log.e(TAG, "MountService not running?");
+ }
+ } break;
}
}
}
@@ -9373,10 +9381,12 @@
}
/*
- * Return true if PackageManager does have packages to be updated.
+ * Update media status on PackageManager.
*/
- public boolean updateExternalMediaStatus(final boolean mediaStatus) {
- final boolean ret;
+ public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Media status can only be updated by the system");
+ }
synchronized (mPackages) {
Log.i(TAG, "Updating external media status from " +
(mMediaMounted ? "mounted" : "unmounted") + " to " +
@@ -9384,32 +9394,29 @@
if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
mediaStatus+", mMediaMounted=" + mMediaMounted);
if (mediaStatus == mMediaMounted) {
- return false;
+ if (reportStatus) {
+ mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
+ }
+ return;
}
mMediaMounted = mediaStatus;
- Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE);
- ret = appList != null && appList.size() > 0;
- if (DEBUG_SD_INSTALL) {
- if (appList != null) {
- for (String app : appList) {
- Log.i(TAG, "Should enable " + app + " on sdcard");
- }
- }
- }
- if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus returning " + ret);
}
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- updateExternalMediaStatusInner(mediaStatus, ret);
+ try {
+ updateExternalMediaStatusInner(mediaStatus);
+ } finally {
+ if (reportStatus) {
+ mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
+ }
+ }
}
});
- return ret;
}
- private void updateExternalMediaStatusInner(boolean mediaStatus,
- boolean sendUpdateBroadcast) {
+ private void updateExternalMediaStatusInner(boolean mediaStatus) {
// If we are up here that means there are packages to be
// enabled or disabled.
final String list[] = PackageHelper.getSecureContainerList();
@@ -9474,11 +9481,11 @@
// Process packages with valid entries.
if (mediaStatus) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, sendUpdateBroadcast, removeCids);
+ loadMediaPackages(processCids, uidArr, removeCids);
startCleaningPackages();
} else {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
- unloadMediaPackages(processCids, uidArr, sendUpdateBroadcast);
+ unloadMediaPackages(processCids, uidArr);
}
}
@@ -9509,8 +9516,7 @@
* to avoid unnecessary crashes.
*/
private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], boolean sendUpdateBroadcast,
- HashSet<String> removeCids) {
+ int uidArr[], HashSet<String> removeCids) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<SdInstallArgs> keys = processCids.keySet();
boolean doGc = false;
@@ -9578,7 +9584,7 @@
mSettings.writeLP();
}
// Send a broadcast to let everyone know we are done processing
- if (sendUpdateBroadcast) {
+ if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(true, pkgList, uidArr);
}
if (doGc) {
@@ -9594,7 +9600,7 @@
}
private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], boolean sendUpdateBroadcast) {
+ int uidArr[]) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
ArrayList<String> pkgList = new ArrayList<String>();
ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
@@ -9617,7 +9623,7 @@
}
}
// Send broadcasts
- if (sendUpdateBroadcast) {
+ if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(false, pkgList, uidArr);
}
// Force gc
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 93e45fc..fdb67f8 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -10755,7 +10755,7 @@
pids[i] = pidCandidates.keyAt(i);
}
try {
- if (mActivityManager.killPidsForMemory(pids)) {
+ if (mActivityManager.killPids(pids, "Free memory")) {
killedApps = true;
}
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2ecebed..7034c88 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8359,11 +8359,11 @@
}
}
- public boolean killPidsForMemory(int[] pids) {
+ public boolean killPids(int[] pids, String pReason) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("killPidsForMemory only available to the system");
+ throw new SecurityException("killPids only available to the system");
}
-
+ String reason = (pReason == null) ? "Unknown" : pReason;
// XXX Note: don't acquire main activity lock here, because the window
// manager calls in with its locks held.
@@ -8387,7 +8387,7 @@
if (worstType < EMPTY_APP_ADJ && worstType > HIDDEN_APP_MIN_ADJ) {
worstType = HIDDEN_APP_MIN_ADJ;
}
- Slog.w(TAG, "Killing processes for memory at adjustment " + worstType);
+ Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
for (int i=0; i<pids.length; i++) {
ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
if (proc == null) {
@@ -8395,10 +8395,10 @@
}
int adj = proc.setAdj;
if (adj >= worstType) {
- Slog.w(TAG, "Killing for memory: " + proc + " (adj "
+ Slog.w(TAG, "Killing " + reason + " : " + proc + " (adj "
+ adj + ")");
- EventLog.writeEvent(EventLogTags.AM_KILL_FOR_MEMORY, proc.pid,
- proc.processName, adj);
+ EventLog.writeEvent(EventLogTags.AM_KILL, proc.pid,
+ proc.processName, adj, reason);
killed = true;
Process.killProcess(pids[i]);
}
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 0ddcc247..aadd37d 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -58,7 +58,7 @@
# The activity's onResume has been called.
30022 am_on_resume_called (Component Name|3)
# Kill a process to reclaim memory.
-30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5)
+30023 am_kill (PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
# Discard an undelivered serialized broadcast (timeout/ANR/crash)
30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
diff --git a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
new file mode 100644
index 0000000..636ba21
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 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.server;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+/**
+ * Tests for {@link com.android.server.EntropyService}
+ */
+public class EntropyServiceTest extends AndroidTestCase {
+
+ public void testInitialWrite() throws Exception {
+ File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE);
+ File file = File.createTempFile("testInitialWrite", "dat", dir);
+ file.deleteOnExit();
+ assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
+
+ // The constructor has the side effect of writing to file
+ new EntropyService("/dev/null", file.getCanonicalPath());
+
+ assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index 6eea46e..d3a34ec 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -202,7 +202,7 @@
/**
* Supply the ICC PIN to the ICC
*
- * When the operation is complete, onComplete will be sent to it's
+ * When the operation is complete, onComplete will be sent to its
* Handler.
*
* onComplete.obj will be an AsyncResult
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index dd7a169..6975c70 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -93,6 +93,9 @@
protected void setUp() throws Exception {
super.setUp();
mOrigState = getMediaState();
+ if (!mountMedia()) {
+ Log.i(TAG, "sdcard not mounted? Some of these tests might fail");
+ }
}
@Override
diff --git a/tests/ConnectivityManagerTest/Android.mk b/tests/ConnectivityManagerTest/Android.mk
new file mode 100644
index 0000000..bd773d0
--- /dev/null
+++ b/tests/ConnectivityManagerTest/Android.mk
@@ -0,0 +1,30 @@
+# Copyright 2010, 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ConnectivityManagerTest
+
+#LOCAL_INSTRUMENTATION_FOR := connectivitymanagertest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ConnectivityManagerTest/AndroidManifest.xml b/tests/ConnectivityManagerTest/AndroidManifest.xml
new file mode 100644
index 0000000..76b58e1
--- /dev/null
+++ b/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.connectivitymanagertest">
+
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="ConnectivityManagerTestActivity"
+ android:label="CMTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.TEST" />
+ </intent-filter>
+ </activity>
+ </application>
+ <!--
+ This declares that this app uses the instrumentation test runner targeting
+ the package of browserpowertest. To run the tests use the command:
+ "adb shell am instrument -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner"
+ -->
+ <instrumentation android:name=".ConnectivityManagerTestRunner"
+ android:targetPackage="com.android.connectivitymanagertest"
+ android:label="Test runner for Connectivity Manager Tests"
+ />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+</manifest>
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
new file mode 100644
index 0000000..1dffa02
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -0,0 +1,329 @@
+package com.android.connectivitymanagertest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import java.util.List;
+import android.widget.LinearLayout;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+
+/**
+ * An activity registered with connectivity manager broadcast
+ * provides network connectivity information and
+ * can be used to set device states: Cellular, Wifi, Airplane mode.
+ */
+public class ConnectivityManagerTestActivity extends Activity {
+
+ public static final String LOG_TAG = "ConnectivityManagerTestActivity";
+ public static final int WAIT_FOR_SCAN_RESULT = 5 * 1000; //5 seconds
+ public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+ public ConnectivityReceiver mConnectivityReceiver = null;
+ public WifiReceiver mWifiReceiver = null;
+ /*
+ * Track network connectivity information
+ */
+ public State mState;
+ public NetworkInfo mNetworkInfo;
+ public NetworkInfo mOtherNetworkInfo;
+ public boolean mIsFailOver;
+ public String mReason;
+ public boolean mScanResultIsAvailable = false;
+ public ConnectivityManager mCM;
+
+ /*
+ * Control Wifi States
+ */
+ public WifiManager mWifiManager;
+
+ /*
+ * Verify connectivity state
+ */
+ public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE;
+ NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
+
+ /**
+ * A wrapper of a broadcast receiver which provides network connectivity information
+ * for all kinds of network: wifi, mobile, etc.
+ */
+ private class ConnectivityReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
+ return;
+ }
+
+ boolean noConnectivity =
+ intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+
+ if (noConnectivity) {
+ mState = State.DISCONNECTED;
+ } else {
+ mState = State.CONNECTED;
+ }
+
+ mNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+
+ mOtherNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
+
+ mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
+ mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
+ recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
+ if (mOtherNetworkInfo != null) {
+ recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
+ }
+ }
+ }
+
+ private class WifiReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ Log.v(LOG_TAG, "onReceive() is calleld with " + intent);
+ return;
+ }
+ notifyScanResult();
+ }
+ }
+
+ public ConnectivityManagerTestActivity() {
+ mState = State.UNKNOWN;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+
+ // Create a simple layout
+ LinearLayout contentView = new LinearLayout(this);
+ contentView.setOrientation(LinearLayout.VERTICAL);
+ setContentView(contentView);
+ setTitle("ConnectivityManagerTestActivity");
+
+ mConnectivityReceiver = new ConnectivityReceiver();
+ // register a connectivity receiver for CONNECTIVITY_ACTION;
+ registerReceiver(mConnectivityReceiver,
+ new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+ mWifiReceiver = new WifiReceiver();
+ registerReceiver(mWifiReceiver,
+ new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ // Get an instance of ConnectivityManager
+ mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Get an instance of WifiManager
+ mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE);
+ initializeNetworkStates();
+
+ if (mWifiManager.isWifiEnabled()) {
+ Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+ clearWifi();
+ }
+ }
+
+ // for each network type, initialize network states to UNKNOWN, and no verification flag is set
+ public void initializeNetworkStates() {
+ for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
+ connectivityState[networkType] = new NetworkState();
+ Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+ connectivityState[networkType].toString());
+ }
+ }
+
+ // deposit a network state
+ public void recordNetworkState(int networkType, State networkState) {
+ Log.v(LOG_TAG, "record network state for network " + networkType +
+ " state is " + networkState);
+ connectivityState[networkType].recordState(networkState);
+ }
+
+ // set the state transition criteria
+ public void setStateTransitionCriteria(int networkType, State initState,
+ int transitionDir, State targetState) {
+ connectivityState[networkType].setStateTransitionCriteria(
+ initState, transitionDir, targetState);
+ }
+
+ // Validate the states recorded
+ public boolean validateNetworkStates(int networkType) {
+ Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+ return connectivityState[networkType].validateStateTransition();
+ }
+
+ // return result from network state validation
+ public String getTransitionFailureReason(int networkType) {
+ Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+ connectivityState[networkType].toString());
+ return connectivityState[networkType].getReason();
+ }
+
+ private void notifyScanResult() {
+ synchronized (this) {
+ Log.v(LOG_TAG, "notify that scan results are available");
+ this.notify();
+ }
+ }
+
+ // Return true if device is currently connected to mobile network
+ public boolean isConnectedToMobile() {
+ return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+ }
+
+ // Return true if device is currently connected to Wifi
+ public boolean isConnectedToWifi() {
+ return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ }
+
+ public boolean enableWifi() {
+ return mWifiManager.setWifiEnabled(true);
+ }
+
+ /**
+ * Associate the device to given SSID
+ * If the device is already associated with a WiFi, disconnect and forget it,
+ * We don't verify whether the connection is successful or not, leave this to the test
+ */
+ public boolean connectToWifi(String knownSSID) {
+ //If Wifi is not enabled, enable it
+ if (!mWifiManager.isWifiEnabled()) {
+ Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+ mWifiManager.setWifiEnabled(true);
+ }
+
+ List<ScanResult> netList = mWifiManager.getScanResults();
+ if (netList == null) {
+ // if no scan results are available, start active scan
+ mWifiManager.startScanActive();
+ mScanResultIsAvailable = false;
+ long startTime = System.currentTimeMillis();
+ while (!mScanResultIsAvailable) {
+ if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+ return false;
+ }
+ // wait for the scan results to be available
+ synchronized (this) {
+ // wait for the scan result to be available
+ try {
+ this.wait(WAIT_FOR_SCAN_RESULT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if ((mWifiManager.getScanResults() == null) ||
+ (mWifiManager.getScanResults().size() <= 0)) {
+ continue;
+ }
+ mScanResultIsAvailable = true;
+ }
+ }
+ }
+
+ netList = mWifiManager.getScanResults();
+ for (int i = 0; i < netList.size(); i++) {
+ ScanResult sr= netList.get(i);
+ if (sr.SSID.equals(knownSSID)) {
+ Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list");
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = sr.SSID;
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ int networkId = mWifiManager.addNetwork(config);
+ mWifiManager.saveConfiguration();
+ // Connect to network by disabling others.
+ mWifiManager.enableNetwork(networkId, true);
+ mWifiManager.reconnect();
+ break;
+ }
+ }
+
+ List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+ if (netConfList.size() <= 0) {
+ Log.v(LOG_TAG, knownSSID + " is not available");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Disable Wifi
+ * @return true if Wifi is disabled successfully
+ */
+ public boolean disableWiFi() {
+ return mWifiManager.setWifiEnabled(false);
+ }
+
+ /**
+ * Disconnect from the current Wifi and clear the configuration list
+ */
+ public boolean clearWifi() {
+ if (mWifiManager.isWifiEnabled()) {
+ //remove the current network Id
+ int curNetworkId = mWifiManager.getConnectionInfo().getNetworkId();
+ mWifiManager.removeNetwork(curNetworkId);
+ mWifiManager.saveConfiguration();
+
+ // remove other saved networks
+ List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+ if (netConfList != null) {
+ Log.v(LOG_TAG, "remove configured network ids");
+ for (int i = 0; i < netConfList.size(); i++) {
+ WifiConfiguration conf = new WifiConfiguration();
+ conf = netConfList.get(i);
+ mWifiManager.removeNetwork(conf.networkId);
+ }
+ }
+ mWifiManager.saveConfiguration();
+ // disable Wifi
+ if (!mWifiManager.setWifiEnabled(false)) {
+ return false;
+ }
+ // wait for the actions to be completed
+ try {
+ Thread.sleep(5*1000);
+ } catch (InterruptedException e) {}
+ }
+ return true;
+ }
+
+ /**
+ * Set airplane mode
+ */
+ public void setAirplaneMode(Context context, boolean enableAM) {
+ //set the airplane mode
+ Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+ enableAM ? 1 : 0);
+ // Post the intent
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", enableAM);
+ context.sendBroadcast(intent);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ //Unregister receiver
+ if (mConnectivityReceiver != null) {
+ unregisterReceiver(mConnectivityReceiver);
+ }
+ if (mWifiReceiver != null) {
+ unregisterReceiver(mWifiReceiver);
+ }
+ Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+ }
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
new file mode 100644
index 0000000..3affa65
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
@@ -0,0 +1,43 @@
+package com.android.connectivitymanagertest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all connectivity manager tests.
+ *
+ * To run the connectivity manager tests:
+ *
+ * adb shell am instrument \
+ * -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ */
+
+public class ConnectivityManagerTestRunner extends InstrumentationTestRunner {
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(ConnectivityManagerMobileTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return ConnectivityManagerTestRunner.class.getClassLoader();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ String testSSID = (String) icicle.get("ssid");
+ if (testSSID != null) {
+ TEST_SSID = testSSID;
+ }
+ }
+
+ public String TEST_SSID = "GoogleGuest";
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
new file mode 100644
index 0000000..925120e
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
@@ -0,0 +1,177 @@
+package com.android.connectivitymanagertest;
+
+import android.net.NetworkInfo.State;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class NetworkState {
+ public static final int TO_DISCONNECTION = 0; // transition to disconnection
+ public static final int TO_CONNECTION = 1; // transition to connection
+ public static final int DO_NOTHING = -1; // no state change
+ private final String LOG_TAG = "NetworkState";
+ private List<State> mStateDepository;
+ private State mTransitionTarget;
+ private int mTransitionDirection;
+ private String mReason = null; // record mReason of state transition failure
+
+ public NetworkState() {
+ mStateDepository = new ArrayList<State>();
+ mTransitionDirection = DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ public NetworkState(State currentState) {
+ mStateDepository = new ArrayList<State>();
+ mStateDepository.add(currentState);
+ mTransitionDirection = DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ // Reinitialize the network state
+ public void resetNetworkState() {
+ mStateDepository.clear();
+ mTransitionDirection = DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ // set the transition criteria, transitionDir could be:
+ // DO_NOTHING, TO_CONNECTION, TO_DISCONNECTION
+ public void setStateTransitionCriteria(State initState, int transitionDir, State targetState) {
+ if (!mStateDepository.isEmpty()) {
+ mStateDepository.clear();
+ }
+ mStateDepository.add(initState);
+ mTransitionDirection = transitionDir;
+ mTransitionTarget = targetState;
+ Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
+ }
+
+ public void recordState(State currentState) {
+ mStateDepository.add(currentState);
+ }
+
+ // Verify state transition
+ public boolean validateStateTransition() {
+ Log.v(LOG_TAG, "print state depository: " + printStates());
+ if (mTransitionDirection == DO_NOTHING) {
+ if (mStateDepository.isEmpty()) {
+ Log.v(LOG_TAG, "no state is recorded");
+ mReason = "no state is recorded.";
+ return false;
+ } else if (mStateDepository.size() > 1) {
+ Log.v(LOG_TAG, "no broadcast is expected, " +
+ "instead broadcast is probably received");
+ mReason = "no broadcast is expected, instead broadcast is probably received";
+ return false;
+ } else if (mStateDepository.get(0) != mTransitionTarget) {
+ Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
+ mStateDepository.get(0));
+ mReason = mTransitionTarget + " is expected, but it is " + mStateDepository.get(0);
+ return false;
+ }
+ return true;
+ } else if (mTransitionDirection == TO_CONNECTION) {
+ Log.v(LOG_TAG, "transition to CONNECTED");
+ return transitToConnection();
+ } else {
+ Log.v(LOG_TAG, "transition to DISCONNECTED");
+ return transitToDisconnection();
+ }
+ }
+
+ /*
+ * Transition from CONNECTED -> DISCONNECTED:
+ * CONNECTED->DISCONNECTING->DISCONNECTED
+ * return false if any state transition is not valid and save a message in mReason
+ */
+ public boolean transitToDisconnection () {
+ mReason = "states: " + printStates();
+ if (mStateDepository.get(0) != State.CONNECTED) {
+ mReason += " initial state should be CONNECTED, but it is " +
+ mStateDepository.get(0) + ".";
+ return false;
+ }
+ State lastState = mStateDepository.get(mStateDepository.size() - 1);
+ if ( lastState != mTransitionTarget) {
+ mReason += " the last state should be DISCONNECTED, but it is " + lastState;
+ return false;
+ }
+ for (int i = 1; i < mStateDepository.size() - 1; i++) {
+ State preState = mStateDepository.get(i-1);
+ State curState = mStateDepository.get(i);
+ if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
+ (curState == State.DISCONNECTED))) {
+ continue;
+ } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
+ continue;
+ } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
+ continue;
+ } else {
+ mReason += " Transition state from " + preState.toString() + " to " +
+ curState.toString() + " is not valid.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // DISCONNECTED->CONNECTING->CONNECTED
+ public boolean transitToConnection() {
+ mReason = "states: " + printStates();
+ if (mStateDepository.get(0) != State.DISCONNECTED) {
+ mReason += " initial state should be DISCONNECTED, but it is " +
+ mStateDepository.get(0) + ".";
+ return false;
+ }
+ State lastState = mStateDepository.get(mStateDepository.size() - 1);
+ if ( lastState != mTransitionTarget) {
+ mReason += " the last state should be CONNECTED, but it is " + lastState;
+ return false;
+ }
+ for (int i = 1; i < mStateDepository.size(); i++) {
+ State preState = mStateDepository.get(i-1);
+ State curState = mStateDepository.get(i);
+ if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
+ (curState == State.CONNECTED))) {
+ continue;
+ } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
+ continue;
+ } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
+ continue;
+ } else {
+ mReason += " Transition state from " + preState.toString() + " to " +
+ curState.toString() + " is not valid.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public List<State> getTransitionStates() {
+ return mStateDepository;
+ }
+
+ // return state failure mReason
+ public String getReason() {
+ return mReason;
+ }
+
+ public String printStates() {
+ StringBuilder stateBuilder = new StringBuilder("");
+ for (int i = 0; i < mStateDepository.size(); i++) {
+ stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
+ }
+ return stateBuilder.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(" ");
+ builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
+ append("; ").append("states:").
+ append(printStates()).append("; ");
+ return builder.toString();
+ }
+}
diff --git a/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
new file mode 100644
index 0000000..ab81bb8
--- /dev/null
+++ b/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -0,0 +1,135 @@
+package com.android.connectivitymanagertest.functional;
+
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkInfo.DetailedState;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.ActivityInstrumentationTestCase2;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
+import com.android.connectivitymanagertest.NetworkState;
+import android.util.Log;
+import junit.framework.*;
+
+public class ConnectivityManagerMobileTest
+ extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+
+ private static final String LOG_TAG = "ConnectivityManagerMobileTest";
+ private static final String PKG_NAME = "com.android.connectivitymanagertest";
+ private static final long WIFI_CONNECTION_TIMEOUT = 30 * 1000;
+ private static final long WIFI_NOTIFICATION_TIMEOUT = 10 * 1000;
+ private String TEST_ACCESS_POINT;
+ private ConnectivityManagerTestActivity cmActivity;
+
+ public ConnectivityManagerMobileTest() {
+ super(PKG_NAME, ConnectivityManagerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ cmActivity = getActivity();
+ ConnectivityManagerTestRunner mRunner =
+ (ConnectivityManagerTestRunner)getInstrumentation();
+ TEST_ACCESS_POINT = mRunner.TEST_SSID;
+ // Each test case will start with cellular connection
+ verifyCellularConnection();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ // clear Wifi after each test case
+ cmActivity.clearWifi();
+ cmActivity.finish();
+ Log.v(LOG_TAG, "tear down ConnectivityManager test activity");
+ super.tearDown();
+ }
+
+ // help function to verify 3G connection
+ public void verifyCellularConnection() {
+ NetworkInfo extraNetInfo = cmActivity.mNetworkInfo;
+ assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
+ extraNetInfo.getType());
+ assertTrue("not connected to cellular network", extraNetInfo.isConnected());
+ assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED));
+ }
+
+ // Test case 1: Test enabling Wifi without associating with any AP
+ @LargeTest
+ public void test3GToWifiNotification() {
+ // As Wifi stays in DISCONNECTED, the connectivity manager will not broadcast
+ // any network connectivity event for Wifi
+ NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+ NetworkState.DO_NOTHING, State.CONNECTED);
+ networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+ NetworkState.DO_NOTHING, State.DISCONNECTED);
+ // Eanble Wifi
+ cmActivity.enableWifi();
+ try {
+ Thread.sleep(WIFI_NOTIFICATION_TIMEOUT);
+ } catch (Exception e) {
+ Log.v(LOG_TAG, "exception: " + e.toString());
+ }
+
+ // validate state and broadcast
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+ Log.v(LOG_TAG, "the state for WIFI is changed");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+ assertTrue(false);
+ }
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+ Log.v(LOG_TAG, "the state for MOBILE is changed");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+ assertTrue(false);
+ }
+ // Verify that the device is still connected to MOBILE
+ verifyCellularConnection();
+ }
+
+ // Test case 2: test connection to a given AP
+ @LargeTest
+ public void testConnectToWifi() {
+ //Prepare for connectivity verification
+ NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+ NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
+ networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+ NetworkState.TO_CONNECTION, State.CONNECTED);
+
+ // Enable Wifi and connect to a test access point
+ assertTrue("failed to connect to " + TEST_ACCESS_POINT,
+ cmActivity.connectToWifi(TEST_ACCESS_POINT));
+ try {
+ Thread.sleep(WIFI_CONNECTION_TIMEOUT);
+ } catch (Exception e) {
+ Log.v(LOG_TAG, "exception: " + e.toString());
+ }
+
+ // validate states
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+ Log.v(LOG_TAG, "Wifi state transition validation failed.");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+ assertTrue(false);
+ }
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+ Log.v(LOG_TAG, "Mobile state transition validation failed.");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+ assertTrue(false);
+ }
+ }
+}