Merge "Fix Camera and GoogleNow launching in keyguard" into jb-mr1-dev
diff --git a/Android.mk b/Android.mk
index a637eb7..07500b1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -74,6 +74,7 @@
core/java/android/app/ISearchManager.aidl \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
+ core/java/android/app/IStopUserCallback.aidl \
core/java/android/app/IThumbnailReceiver.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 47d6a02..7f3dbe5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,7 +31,6 @@
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -141,6 +140,8 @@
runToUri(true);
} else if (op.equals("switch-user")) {
runSwitchUser();
+ } else if (op.equals("stop-user")) {
+ runStopUser();
} else {
throw new IllegalArgumentException("Unknown command: " + op);
}
@@ -323,7 +324,6 @@
mUserId = Integer.parseInt(nextArgRequired());
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return null;
}
}
@@ -594,7 +594,6 @@
no_window_animation = true;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -738,7 +737,6 @@
persistent = true;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -752,13 +750,27 @@
}
private void runSwitchUser() throws Exception {
- if (android.os.Process.myUid() != 0) {
- throw new RuntimeException("switchuser can only be run as root");
- }
String user = nextArgRequired();
mAm.switchUser(Integer.parseInt(user));
}
+ private void runStopUser() throws Exception {
+ String user = nextArgRequired();
+ int res = mAm.stopUser(Integer.parseInt(user), null);
+ if (res != ActivityManager.USER_OP_SUCCESS) {
+ String txt = "";
+ switch (res) {
+ case ActivityManager.USER_OP_IS_CURRENT:
+ txt = " (Can't stop current user)";
+ break;
+ case ActivityManager.USER_OP_UNKNOWN_USER:
+ txt = " (Unknown user " + user + ")";
+ break;
+ }
+ System.err.println("Switch failed: " + res + txt);
+ }
+ }
+
class MyActivityController extends IActivityController.Stub {
final String mGdbPort;
@@ -1047,7 +1059,6 @@
gdbPort = nextArgRequired();
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -1065,7 +1076,6 @@
enabled = false;
} else {
System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
- showUsage();
return;
}
@@ -1090,7 +1100,6 @@
int div = size.indexOf('x');
if (div <= 0 || div >= (size.length()-1)) {
System.err.println("Error: bad size " + size);
- showUsage();
return;
}
String mstr = size.substring(0, div);
@@ -1100,7 +1109,6 @@
n = Integer.parseInt(nstr);
} catch (NumberFormatException e) {
System.err.println("Error: bad number " + e);
- showUsage();
return;
}
}
@@ -1139,12 +1147,10 @@
density = Integer.parseInt(densityStr);
} catch (NumberFormatException e) {
System.err.println("Error: bad number " + e);
- showUsage();
return;
}
if (density < 72) {
System.err.println("Error: density must be >= 72");
- showUsage();
return;
}
}
@@ -1345,6 +1351,7 @@
" am to-uri [INTENT]\n" +
" am to-intent-uri [INTENT]\n" +
" am switch-user <USER_ID>\n" +
+ " am stop-user <USER_ID>\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -1403,6 +1410,12 @@
"\n" +
"am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
"\n" +
+ "am switch-user: switch to put USER_ID in the foreground, starting" +
+ " execution of that user if it is currently stopped.\n" +
+ "\n" +
+ "am stop-user: stop execution of USER_ID, not allowing it to run any" +
+ " code until a later explicit switch to it.\n" +
+ "\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f8a9d75..26d8c17 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -211,6 +211,15 @@
*/
public static final int INTENT_SENDER_SERVICE = 4;
+ /** @hide User operation call: success! */
+ public static final int USER_OP_SUCCESS = 0;
+
+ /** @hide User operation call: given user id is not known. */
+ public static final int USER_OP_UNKNOWN_USER = -1;
+
+ /** @hide User operation call: given user id is the current user, can't be stopped. */
+ public static final int USER_OP_IS_CURRENT = -2;
+
/*package*/ ActivityManager(Context context, Handler handler) {
mContext = context;
mHandler = handler;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index adc9434..05c009f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1570,6 +1570,17 @@
return true;
}
+ case STOP_USER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int userid = data.readInt();
+ IStopUserCallback callback = IStopUserCallback.Stub.asInterface(
+ data.readStrongBinder());
+ int result = stopUser(userid, callback);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case GET_CURRENT_USER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
UserInfo userInfo = getCurrentUser();
@@ -3756,11 +3767,25 @@
return result;
}
+ public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(userid);
+ data.writeStrongInterface(callback);
+ mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
public UserInfo getCurrentUser() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+ mRemote.transact(GET_CURRENT_USER_TRANSACTION, data, reply, 0);
reply.readException();
UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c3e911e..70d8445 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -331,6 +331,7 @@
// Multi-user APIs
public boolean switchUser(int userid) throws RemoteException;
+ public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
@@ -611,4 +612,5 @@
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
int IS_INTENT_SENDER_AN_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+151;
int START_ACTIVITY_AS_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+152;
+ int STOP_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+153;
}
diff --git a/core/java/android/app/IStopUserCallback.aidl b/core/java/android/app/IStopUserCallback.aidl
new file mode 100644
index 0000000..19ac1d5
--- /dev/null
+++ b/core/java/android/app/IStopUserCallback.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright 2012, 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 android.app;
+
+/**
+ * Callback to find out when we have finished stopping a user.
+ * {@hide}
+ */
+interface IStopUserCallback
+{
+ void userStopped(int userId);
+ void userStopAborted(int userId);
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 06edf32..53e0a75 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
-import android.media.RemoteControlClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -2287,6 +2286,15 @@
"android.intent.action.USER_ADDED";
/**
+ * Broadcast sent to the system when a user is stopped. Carries an extra EXTRA_USER_HANDLE that has
+ * the userHandle of the user. This is similar to {@link #ACTION_PACKAGE_RESTARTED},
+ * but for an entire user instead of a specific package.
+ * @hide
+ */
+ public static final String ACTION_USER_STOPPED =
+ "android.intent.action.USER_STOPPED";
+
+ /**
* Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
* the userHandle of the user.
* @hide
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 650681a..3477a90 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -49,6 +49,7 @@
sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
sPackageFilt.addDataScheme("package");
sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+ sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
}
@@ -136,6 +137,9 @@
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
return false;
}
+
+ public void onHandleUserStop(Intent intent, int userHandle) {
+ }
public void onUidRemoved(int uid) {
}
@@ -307,6 +311,10 @@
intent.getIntExtra(Intent.EXTRA_UID, 0), true);
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
+ onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ }
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
mAppearingPackages = pkgList;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5829787..96eb54a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -62,6 +62,7 @@
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_STOPPED" />
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
new file mode 100644
index 0000000..c30eb1c
--- /dev/null
+++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
new file mode 100644
index 0000000..e5d5771
--- /dev/null
+++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
Binary files differ
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index ca0fa1d..b404155 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -26,7 +26,7 @@
android:layout_height="match_parent"
android:orientation="horizontal">
- <com.android.internal.policy.impl.keyguard.KeyguardWidgetView
+ <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
android:id="@+id/app_widget_container"
android:layout_width="0dip"
android:layout_height="match_parent"
@@ -35,8 +35,8 @@
<!-- TODO: Remove this once supported as a widget -->
<include layout="@layout/keyguard_status_view"/>
+ </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
- </com.android.internal.policy.impl.keyguard.KeyguardWidgetView>
<ViewFlipper
android:id="@+id/view_flipper"
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
index 6f6611d..d516369 100644
--- a/core/res/res/layout/keyguard_selector_view.xml
+++ b/core/res/res/layout/keyguard_selector_view.xml
@@ -26,7 +26,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <com.android.internal.policy.impl.keyguard.KeyguardWidgetView
+ <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
android:id="@+id/app_widget_container"
android:layout_width="match_parent"
android:layout_height="0dip"
@@ -34,7 +34,7 @@
android:visibility="gone">
<!-- TODO: Remove this once supported as a widget -->
<include layout="@layout/keyguard_status_view"/>
- </com.android.internal.policy.impl.keyguard.KeyguardWidgetView>
+ </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
<RelativeLayout
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index 3560ea2..f8d05b7 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -18,67 +18,85 @@
-->
<!-- This is a view that shows general status information in Keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardStatusView
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
- <com.android.internal.widget.DigitalClock android:id="@+id/time"
- android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin"
- android:layout_marginBottom="12dip"
- android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
- android:layout_gravity="end">
+ <com.android.internal.policy.impl.keyguard.KeyguardStatusView
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal">
- <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
- top of the other. Hence the redundant layout... -->
- <TextView android:id="@*android:id/timeDisplayBackground"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="none"
- android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_marginBottom="6dip"
- android:textColor="@*android:color/lockscreen_clock_background"
- />
+ <com.android.internal.widget.DigitalClock android:id="@+id/time"
+ android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin"
+ android:layout_marginBottom="12dip"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:layout_gravity="end">
- <TextView android:id="@*android:id/timeDisplayForeground"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="none"
- android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_marginBottom="6dip"
- android:textColor="@*android:color/lockscreen_clock_foreground"
- android:layout_alignStart="@*android:id/timeDisplayBackground"
- android:layout_alignTop="@*android:id/timeDisplayBackground"
- />
+ <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+ top of the other. Hence the redundant layout... -->
+ <TextView android:id="@*android:id/timeDisplayBackground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="6dip"
+ android:textColor="@*android:color/lockscreen_clock_background"
+ />
- </com.android.internal.widget.DigitalClock>
+ <TextView android:id="@*android:id/timeDisplayForeground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="6dip"
+ android:textColor="@*android:color/lockscreen_clock_foreground"
+ android:layout_alignStart="@*android:id/timeDisplayBackground"
+ android:layout_alignTop="@*android:id/timeDisplayBackground"
+ />
- <LinearLayout
- android:orientation="horizontal"
- android:layout_gravity="end"
- android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin">
+ </com.android.internal.widget.DigitalClock>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin">
+
+ <TextView
+ android:id="@*android:id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ />
+
+ <TextView
+ android:id="@*android:id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawablePadding="4dip"
+ />
+
+ </LinearLayout>
<TextView
- android:id="@*android:id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
- />
-
- <TextView
- android:id="@*android:id/alarm_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dip"
+ android:id="@*android:id/status1"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceMedium"
@@ -86,29 +104,16 @@
android:drawablePadding="4dip"
/>
- </LinearLayout>
+ <TextView
+ android:id="@*android:id/carrier"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ />
- <TextView
- android:id="@*android:id/status1"
- android:layout_gravity="end"
- android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
- android:drawablePadding="4dip"
- />
-
- <TextView
- android:id="@*android:id/carrier"
- android:layout_gravity="end"
- android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- />
-
-</com.android.internal.policy.impl.keyguard.KeyguardStatusView>
-
+ </com.android.internal.policy.impl.keyguard.KeyguardStatusView>
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cc4d05a..372a1ee 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -251,4 +251,6 @@
<!-- Height of widget view in keyguard. -->
<dimen name="kg_widget_view_height">0dp</dimen>
+ <!-- Padding surrounding each widget page -->
+ <dimen name="kg_widget_page_padding">10dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b110a40..6414df8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1237,6 +1237,7 @@
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
<java-symbol type="dimen" name="status_bar_height" />
+ <java-symbol type="dimen" name="kg_widget_page_padding" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1254,6 +1255,8 @@
<java-symbol type="drawable" name="jog_tab_target_yellow" />
<java-symbol type="drawable" name="menu_background" />
<java-symbol type="drawable" name="stat_sys_secure" />
+ <java-symbol type="drawable" name="kg_widget_overscroll_layer_left" />
+ <java-symbol type="drawable" name="kg_widget_overscroll_layer_right" />
<java-symbol type="id" name="action_mode_bar_stub" />
<java-symbol type="id" name="alarm_status" />
<java-symbol type="id" name="backspace" />
@@ -3749,5 +3752,5 @@
<public type="attr" name="listPreferredItemPaddingStart" />
<public type="attr" name="listPreferredItemPaddingEnd" />
<public type="attr" name="singleUser" />
-
+
</resources>
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 1e425ea..d74a5e7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -70,7 +70,7 @@
private static final int SECURITY_ACCOUNT_ID = R.id.keyguard_account_view;
private AppWidgetHost mAppWidgetHost;
- private ViewGroup mAppWidgetContainer;
+ private KeyguardWidgetPager mAppWidgetContainer;
private ViewFlipper mViewFlipper;
private Button mEmergencyDialerButton;
private boolean mEnableMenuKey;
@@ -121,7 +121,7 @@
@Override
protected void onFinishInflate() {
- mAppWidgetContainer = (ViewGroup) findViewById(R.id.app_widget_container);
+ mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
mAppWidgetContainer.setVisibility(VISIBLE);
// View Flipper
@@ -182,7 +182,7 @@
}
void addWidget(AppWidgetHostView view) {
- mAppWidgetContainer.addView(view);
+ mAppWidgetContainer.addWidget(view);
}
private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
new file mode 100644
index 0000000..d778129
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 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.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+
+public class KeyguardWidgetFrame extends FrameLayout {
+ private final static PorterDuffXfermode sAddBlendMode =
+ new PorterDuffXfermode(PorterDuff.Mode.ADD);
+ private static int sWidgetPagePadding;
+ private static Drawable sLeftOverscrollDrawable;
+ private static Drawable sRightOverscrollDrawable;
+
+ private Drawable mForegroundDrawable;
+ private final Rect mForegroundRect = new Rect();
+ private int mForegroundAlpha = 0;
+
+ public KeyguardWidgetFrame(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardWidgetFrame(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ if (sLeftOverscrollDrawable == null) {
+ Resources res = context.getResources();
+ sLeftOverscrollDrawable = res.getDrawable(
+ com.android.internal.R.drawable.kg_widget_overscroll_layer_left);
+ sRightOverscrollDrawable = res.getDrawable(
+ com.android.internal.R.drawable.kg_widget_overscroll_layer_right);
+ sWidgetPagePadding =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.kg_widget_page_padding);
+ }
+ setPadding(sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mForegroundAlpha > 0) {
+ mForegroundDrawable.setBounds(mForegroundRect);
+ Paint p = ((NinePatchDrawable) mForegroundDrawable).getPaint();
+ p.setXfermode(sAddBlendMode);
+ mForegroundDrawable.draw(canvas);
+ p.setXfermode(null);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mForegroundRect.set(sWidgetPagePadding, sWidgetPagePadding,
+ w - sWidgetPagePadding, h - sWidgetPagePadding);
+ }
+
+ void setOverScrollAmount(float r, boolean left) {
+ if (left && mForegroundDrawable != sLeftOverscrollDrawable) {
+ mForegroundDrawable = sLeftOverscrollDrawable;
+ } else if (!left && mForegroundDrawable != sRightOverscrollDrawable) {
+ mForegroundDrawable = sRightOverscrollDrawable;
+ }
+
+ mForegroundAlpha = (int) Math.round((r * 255));
+ mForegroundDrawable.setAlpha(mForegroundAlpha);
+ invalidate();
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
similarity index 76%
rename from policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java
rename to policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 120f8f8..7d077e2 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -16,39 +16,53 @@
package com.android.internal.policy.impl.keyguard;
import android.animation.TimeInterpolator;
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
-public class KeyguardWidgetView extends PagedView {
+import android.widget.FrameLayout;
- public KeyguardWidgetView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardWidgetView(Context context) {
- this(null, null, 0);
- }
-
- public KeyguardWidgetView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- }
-
+public class KeyguardWidgetPager extends PagedView {
ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
- private static float CAMERA_DISTANCE = 1500;
+ private static float CAMERA_DISTANCE = 10000;
private static float TRANSITION_SCALE_FACTOR = 0.74f;
private static float TRANSITION_PIVOT = 0.65f;
private static float TRANSITION_MAX_ROTATION = 30;
private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
+
+ public KeyguardWidgetPager(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetPager(Context context) {
+ this(null, null, 0);
+ }
+
+ public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /*
+ * We wrap widgets in a special frame which handles drawing the overscroll foreground.
+ */
+ public void addWidget(AppWidgetHostView widget) {
+ KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ lp.gravity = Gravity.CENTER;
+ // The framework adds a default padding to AppWidgetHostView. We don't need this padding
+ // for the Keyguard, so we override it to be 0.
+ widget.setPadding(0, 0, 0, 0);
+ frame.addView(widget, lp);
+ addView(frame);
+ }
+
/*
* This interpolator emulates the rate at which the perceived scale of an object changes
* as its distance from a camera increases. When this interpolator is applied to a scale
@@ -68,6 +82,11 @@
}
}
+ @Override
+ protected void overScroll(float amount) {
+ acceleratedOverScroll(amount);
+ }
+
// In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
@Override
protected void screenScrolled(int screenCenter) {
@@ -103,6 +122,10 @@
// Overscroll to the left
v.setPivotX(TRANSITION_PIVOT * pageWidth);
v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
+ if (v instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
+ true);
+ }
scale = 1.0f;
alpha = 1.0f;
// On the first page, we don't want the page to have any lateral motion
@@ -113,6 +136,10 @@
v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
scale = 1.0f;
alpha = 1.0f;
+ if (v instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
+ false);
+ }
// On the last page, we don't want the page to have any lateral motion.
translationX = 0;
} else {
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 32ac8e1..9b7be02 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -34,6 +34,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
import android.text.format.Time;
@@ -303,7 +304,7 @@
}
}
}
-
+
public void removeLocked(String packageName) {
removeLocked(mRtcWakeupAlarms, packageName);
removeLocked(mRtcAlarms, packageName);
@@ -327,6 +328,29 @@
}
}
}
+
+ public void removeUserLocked(int userHandle) {
+ removeUserLocked(mRtcWakeupAlarms, userHandle);
+ removeUserLocked(mRtcAlarms, userHandle);
+ removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
+ removeUserLocked(mElapsedRealtimeAlarms, userHandle);
+ }
+
+ private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
+ if (alarmList.size() <= 0) {
+ return;
+ }
+
+ // iterator over the list removing any it where the intent match
+ Iterator<Alarm> it = alarmList.iterator();
+
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) {
+ it.remove();
+ }
+ }
+ }
public boolean lookForPackageLocked(String packageName) {
return lookForPackageLocked(mRtcWakeupAlarms, packageName)
@@ -822,6 +846,7 @@
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiver(this, sdFilter);
}
@@ -841,6 +866,11 @@
return;
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ removeUserLocked(userHandle);
+ }
} else {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
&& intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index d6fed39..9b61ec4 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -544,6 +544,11 @@
mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK));
updateNotificationPulse();
+ } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ cancelAllNotificationsUser(userHandle);
+ }
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
@@ -619,6 +624,7 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -1249,6 +1255,29 @@
}
}
+ /**
+ * Cancels all notifications from a given user.
+ */
+ boolean cancelAllNotificationsUser(int userHandle) {
+ synchronized (mNotificationList) {
+ final int N = mNotificationList.size();
+ boolean canceledSomething = false;
+ for (int i = N-1; i >= 0; --i) {
+ NotificationRecord r = mNotificationList.get(i);
+ if (UserHandle.getUserId(r.uid) != userHandle) {
+ continue;
+ }
+ canceledSomething = true;
+ mNotificationList.remove(i);
+ cancelNotificationLocked(r, false);
+ }
+ if (canceledSomething) {
+ updateLightsLocked();
+ }
+ return canceledSomething;
+ }
+ }
+
@Deprecated
public void cancelNotification(String pkg, int id) {
cancelNotificationWithTag(pkg, null /* tag */, id);
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index ca7faa2..b0dfa80 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -989,6 +989,17 @@
// restarting state.
mRestartingServices.remove(r);
+ // Make sure that the user who owns this service is started. If not,
+ // we don't want to allow it to run.
+ if (mAm.mStartedUsers.get(r.userId) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + r.appInfo.packageName + "/"
+ + r.appInfo.uid + " for service "
+ + r.intent.getIntent() + ": user " + r.userId + " is stopped");
+ bringDownServiceLocked(r, true);
+ return false;
+ }
+
// Service is now being launched, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
@@ -1509,7 +1520,7 @@
boolean didSomething = false;
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
- if (service.packageName.equals(name)
+ if ((name == null || service.packageName.equals(name))
&& (service.app == null || evenPersistent || !service.app.persistent)) {
if (!doit) {
return true;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 368db26..d8c8dfa 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -46,6 +46,7 @@
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
+import android.app.IStopUserCallback;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Notification;
@@ -428,6 +429,11 @@
long mPreviousProcessVisibleTime;
/**
+ * Which uses have been started, so are allowed to run code.
+ */
+ final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+
+ /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -791,7 +797,6 @@
static ActivityThread mSystemThread;
private int mCurrentUserId;
- private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
private UserManager mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1506,6 +1511,9 @@
systemDir, "usagestats").toString());
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
+ // User 0 is the first and only user that runs at boot.
+ mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -2095,7 +2103,7 @@
}
}
- boolean startHomeActivityLocked(int userId) {
+ boolean startHomeActivityLocked(int userId, UserStartedState startingUser) {
if (mHeadless) {
// Added because none of the other calls to ensureBootCompleted seem to fire
// when running headless.
@@ -2135,6 +2143,9 @@
null, null, 0, 0, 0, 0, null, false, null);
}
}
+ if (startingUser != null) {
+ mMainStack.addStartingUserLocked(startingUser);
+ }
return true;
}
@@ -3454,7 +3465,7 @@
Slog.w(TAG, "Invalid packageName: " + packageName);
return;
}
- killPackageProcessesLocked(packageName, pkgUid,
+ killPackageProcessesLocked(packageName, pkgUid, -1,
ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
}
} finally {
@@ -3650,7 +3661,8 @@
}
private void forceStopPackageLocked(final String packageName, int uid) {
- forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid));
+ forceStopPackageLocked(packageName, uid, false, false, true, false,
+ UserHandle.getUserId(uid));
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -3662,16 +3674,27 @@
false, false,
MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
-
+
+ private void forceStopUserLocked(int userId) {
+ forceStopPackageLocked(null, -1, false, false, true, false, userId);
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false,
+ MY_PID, Process.SYSTEM_UID, userId);
+ }
+
private final boolean killPackageProcessesLocked(String packageName, int uid,
- int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit,
- boolean evenPersistent, String reason) {
+ int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+ boolean doit, boolean evenPersistent, String reason) {
ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
// matches the package name.
- final String procNamePrefix = packageName + ":";
+ final String procNamePrefix = packageName != null ? (packageName + ":") : null;
for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
@@ -3684,6 +3707,18 @@
if (doit) {
procs.add(app);
}
+ // If no package is specified, we call all processes under the
+ // give user id.
+ } else if (packageName == null) {
+ if (app.userId == userId) {
+ if (app.setAdj >= minOomAdj) {
+ if (!doit) {
+ return true;
+ }
+ app.removed = true;
+ procs.add(app);
+ }
+ }
// If uid is specified and the uid and process name match
// Or, the uid is not specified and the process name matches
} else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
@@ -3714,7 +3749,7 @@
int i;
int N;
- if (uid < 0) {
+ if (uid < 0 && name != null) {
try {
uid = AppGlobals.getPackageManager().getPackageUid(name, userId);
} catch (RemoteException e) {
@@ -3722,24 +3757,45 @@
}
if (doit) {
- Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+ if (name != null) {
+ Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+ } else {
+ Slog.i(TAG, "Force stopping user " + userId);
+ }
Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
while (badApps.hasNext()) {
SparseArray<Long> ba = badApps.next();
- if (ba.get(uid) != null) {
+ for (i=ba.size()-1; i>=0; i--) {
+ boolean remove = false;
+ final int entUid = ba.keyAt(i);
+ if (name != null) {
+ if (entUid == uid) {
+ remove = true;
+ }
+ } else if (UserHandle.getUserId(entUid) == userId) {
+ remove = true;
+ }
+ if (remove) {
+ ba.removeAt(i);
+ }
+ }
+ if (ba.size() == 0) {
badApps.remove();
}
}
}
-
- boolean didSomething = killPackageProcessesLocked(name, uid, -100,
- callerWillRestart, false, doit, evenPersistent, "force stop");
+
+ boolean didSomething = killPackageProcessesLocked(name, uid,
+ name == null ? userId : -1 , -100, callerWillRestart, false,
+ doit, evenPersistent,
+ name == null ? ("force stop user " + userId) : ("force stop " + name));
TaskRecord lastTask = null;
for (i=0; i<mMainStack.mHistory.size(); i++) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- final boolean samePackage = r.packageName.equals(name);
+ final boolean samePackage = r.packageName.equals(name)
+ || (name == null && r.userId == userId);
if (r.userId == userId
&& (samePackage || r.task == lastTask)
&& (r.app == null || evenPersistent || !r.app.persistent)) {
@@ -3776,7 +3832,7 @@
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
- if (provider.info.packageName.equals(name)
+ if ((name == null || provider.info.packageName.equals(name))
&& (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
if (!doit) {
return true;
@@ -3792,7 +3848,7 @@
}
if (doit) {
- if (purgeCache) {
+ if (purgeCache && name != null) {
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(name);
@@ -4197,15 +4253,6 @@
}
}, pkgFilter);
- IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onUserRemoved(intent);
- }
- }, userFilter);
-
synchronized (this) {
// Ensure that any processes we had put on hold are now started
// up.
@@ -4227,13 +4274,17 @@
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
- List<UserInfo> users = getUserManager().getUsers();
- for (UserInfo user : users) {
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
- null, null, 0, null, null,
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID, user.id);
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ if (uss.mState == UserStartedState.STATE_BOOTING) {
+ uss.mState = UserStartedState.STATE_RUNNING;
+ broadcastIntentLocked(null, null,
+ new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID,
+ mStartedUsers.keyAt(i));
+ }
}
}
}
@@ -6297,6 +6348,16 @@
"Attempt to launch content provider before system ready");
}
+ // Make sure that the user who owns this provider is started. If not,
+ // we don't want to allow it to run.
+ if (mStartedUsers.get(userId) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": user " + userId + " is stopped");
+ return null;
+ }
+
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
final boolean firstClass = cpr == null;
@@ -9048,6 +9109,13 @@
}
pw.println();
+ pw.println(" mStartedUsers:");
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
+ pw.println(":");
+ uss.dump(" ", pw);
+ }
pw.println(" mHomeProcess: " + mHomeProcess);
pw.println(" mPreviousProcess: " + mPreviousProcess);
if (dumpAll) {
@@ -11107,6 +11175,14 @@
}
}
+ // Make sure that the user who is receiving this broadcast is started
+ // If not, we will just skip it.
+ if (mStartedUsers.get(userId) == null) {
+ Slog.w(TAG, "Skipping broadcast of " + intent
+ + ": user " + userId + " is stopped");
+ return ActivityManager.BROADCAST_SUCCESS;
+ }
+
// Handle special intents: if this broadcast is from the package
// manager about a package being removed, we need to remove all of
// its activities from the history stack.
@@ -11649,7 +11725,7 @@
app.instrumentationProfileFile = null;
app.instrumentationArguments = null;
- forceStopPackageLocked(app.processName, -1, false, false, true, true, app.userId);
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId);
}
public void finishInstrumentation(IApplicationThread target,
@@ -13491,73 +13567,172 @@
// Multi-user methods
+ @Override
public boolean switchUser(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- Slog.e(TAG, "Trying to switch user from unauthorized app");
- return false;
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
- if (mCurrentUserId == userId)
- return true;
-
synchronized (this) {
- // Check if user is already logged in, otherwise check if user exists first before
- // adding to the list of logged in users.
- if (mLoggedInUsers.indexOfKey(userId) < 0) {
- if (!userExists(userId)) {
- return false;
- }
- mLoggedInUsers.append(userId, userId);
+ if (mCurrentUserId == userId) {
+ return true;
+ }
+
+ // If the user we are switching to is not currently started, then
+ // we need to start it now.
+ if (mStartedUsers.get(userId) == null) {
+ mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
}
mCurrentUserId = userId;
boolean haveActivities = mMainStack.switchUser(userId);
if (!haveActivities) {
- startHomeActivityLocked(userId);
+ startHomeActivityLocked(userId, mStartedUsers.get(userId));
}
-
}
- // Inform of user switch
- Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Inform of user switch
+ Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
return true;
}
- @Override
- public UserInfo getCurrentUser() throws RemoteException {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- Slog.e(TAG, "Trying to get user from unauthorized app");
- return null;
+ void finishUserSwitch(UserStartedState uss) {
+ synchronized (this) {
+ if (uss.mState == UserStartedState.STATE_BOOTING
+ && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
+ uss.mState = UserStartedState.STATE_RUNNING;
+ broadcastIntentLocked(null, null,
+ new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, uss.mHandle.getIdentifier());
+ }
}
- return getUserManager().getUserInfo(mCurrentUserId);
}
- private void onUserRemoved(Intent intent) {
- int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (extraUserId < 1) return;
-
- // Kill all the processes for the user
- ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>();
+ @Override
+ public int stopUser(final int userId, final IStopUserCallback callback) {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (userId <= 0) {
+ throw new IllegalArgumentException("Can't stop primary user " + userId);
+ }
synchronized (this) {
- HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap();
- for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
- SparseArray<ProcessRecord> uids = uidMap.getValue();
- for (int i = 0; i < uids.size(); i++) {
- if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) {
- pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
- }
+ if (mCurrentUserId == userId) {
+ return ActivityManager.USER_OP_IS_CURRENT;
+ }
+
+ final UserStartedState uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ // User is not started, nothing to do... but we do need to
+ // callback if requested.
+ if (callback != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ });
+ }
+ return ActivityManager.USER_OP_SUCCESS;
+ }
+
+ if (callback != null) {
+ uss.mStopCallbacks.add(callback);
+ }
+
+ if (uss.mState != UserStartedState.STATE_STOPPING) {
+ uss.mState = UserStartedState.STATE_STOPPING;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Inform of user switch
+ Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
+ final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky) {
+ finishUserStop(uss);
+ }
+ };
+ broadcastIntentLocked(null, null, intent,
+ null, resultReceiver, 0, null, null, null,
+ true, false, MY_PID, Process.SYSTEM_UID, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
+ }
- for (Pair<String,Integer> pkgAndUid : pkgAndUids) {
- forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second,
- false, false, true, true, extraUserId);
+ return ActivityManager.USER_OP_SUCCESS;
+ }
+
+ void finishUserStop(UserStartedState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ boolean stopped;
+ ArrayList<IStopUserCallback> callbacks;
+ synchronized (this) {
+ callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
+ if (uss.mState != UserStartedState.STATE_STOPPING
+ || mStartedUsers.get(userId) != uss) {
+ stopped = false;
+ } else {
+ stopped = true;
+ // User can no longer run.
+ mStartedUsers.remove(userId);
+
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUserLocked(userId);
}
}
+
+ for (int i=0; i<callbacks.size(); i++) {
+ try {
+ if (stopped) callbacks.get(i).userStopped(userId);
+ else callbacks.get(i).userStopAborted(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public UserInfo getCurrentUser() {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: getCurrentUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (this) {
+ return getUserManager().getUserInfo(mCurrentUserId);
+ }
}
private boolean userExists(int userId) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index ccea41a..a389edc2 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -211,7 +211,10 @@
*/
final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
= new ArrayList<IActivityManager.WaitResult>();
-
+
+ final ArrayList<UserStartedState> mStartingUsers
+ = new ArrayList<UserStartedState>();
+
/**
* Set when the system is going to sleep, until we have
* successfully paused the current activity and released our wake lock.
@@ -1397,7 +1400,7 @@
// Launcher...
if (mMainStack) {
ActivityOptions.abort(options);
- return mService.startHomeActivityLocked(0);
+ return mService.startHomeActivityLocked(0, null);
}
}
@@ -1427,7 +1430,16 @@
ActivityOptions.abort(options);
return false;
}
-
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (mService.mStartedUsers.get(next.userId) == null) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.userId + " is stopped");
+ return false;
+ }
+
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mStoppingActivities.remove(next);
@@ -1494,7 +1506,7 @@
Slog.d(TAG, "no-history finish of " + last + " on new resume");
}
requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null,
- "no-history");
+ "no-history");
}
}
@@ -3414,6 +3426,7 @@
ArrayList<ActivityRecord> stops = null;
ArrayList<ActivityRecord> finishes = null;
ArrayList<ActivityRecord> thumbnails = null;
+ ArrayList<UserStartedState> startingUsers = null;
int NS = 0;
int NF = 0;
int NT = 0;
@@ -3495,6 +3508,10 @@
booting = mService.mBooting;
mService.mBooting = false;
}
+ if (mStartingUsers.size() > 0) {
+ startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+ mStartingUsers.clear();
+ }
}
int i;
@@ -3539,6 +3556,10 @@
if (booting) {
mService.finishBooting();
+ } else if (startingUsers != null) {
+ for (i=0; i<startingUsers.size(); i++) {
+ mService.finishUserSwitch(startingUsers.get(i));
+ }
}
mService.trimApplications();
@@ -3556,6 +3577,10 @@
return res;
}
+ final void addStartingUserLocked(UserStartedState uss) {
+ mStartingUsers.add(uss);
+ }
+
/**
* @return Returns true if the activity is being finished, false if for
* some reason it is being left as-is.
diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java
new file mode 100644
index 0000000..3f3ed85
--- /dev/null
+++ b/services/java/com/android/server/am/UserStartedState.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.am;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.IStopUserCallback;
+import android.os.UserHandle;
+
+public class UserStartedState {
+ public final static int STATE_BOOTING = 0;
+ public final static int STATE_RUNNING = 1;
+ public final static int STATE_STOPPING = 2;
+
+ public final UserHandle mHandle;
+ public final ArrayList<IStopUserCallback> mStopCallbacks
+ = new ArrayList<IStopUserCallback>();
+
+ public int mState = STATE_BOOTING;
+
+ public UserStartedState(UserHandle handle, boolean initial) {
+ mHandle = handle;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mState="); pw.println(mState);
+ }
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 3c5eed7..48a4b74 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1818,7 +1818,7 @@
pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.sourceDir = ps.codePathString;
pkg.applicationInfo.dataDir =
- getDataPathForPackage(ps.pkg.packageName, 0).getPath();
+ getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
// pkg.mSetEnabled = ps.getEnabled(userId);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 750aa72..fb04d0f1 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -23,6 +23,8 @@
import com.android.internal.util.FastXmlSerializer;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IStopUserCallback;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -33,6 +35,7 @@
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AtomicFile;
import android.util.Slog;
@@ -549,13 +552,36 @@
*/
public boolean removeUser(int userHandle) {
checkManageUsersPermission("Only the system can remove users");
+ final UserInfo user;
+ synchronized (mPackagesLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+ }
+
+ int res;
+ try {
+ res = ActivityManagerNative.getDefault().stopUser(userHandle,
+ new IStopUserCallback.Stub() {
+ @Override
+ public void userStopped(int userId) {
+ finishRemoveUser(userId);
+ }
+ @Override
+ public void userStopAborted(int userId) {
+ }
+ });
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ return res == ActivityManager.USER_OP_SUCCESS;
+ }
+
+ void finishRemoveUser(int userHandle) {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
- final UserInfo user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null) {
- return false;
- }
-
// Cleanup package manager settings
mPm.cleanUpUserLILPw(userHandle);
@@ -574,7 +600,6 @@
Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
- return true;
}
@Override