summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2013-06-21 21:34:38 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-06-21 21:34:38 +0000
commite539ee865f06e9190dea38b7e277b096a608efd6 (patch)
treeac62ea18db40900b15493af9657e5a0e163d9be7 /packages/SystemUI
parent4a7052a7755bbcc332924ebcfaff8402c0613c7b (diff)
parent5c4541246c6a70f53552423dc35940386788bd5f (diff)
downloadframeworks_base-e539ee865f06e9190dea38b7e277b096a608efd6.zip
frameworks_base-e539ee865f06e9190dea38b7e277b096a608efd6.tar.gz
frameworks_base-e539ee865f06e9190dea38b7e277b096a608efd6.tar.bz2
Merge "Introduce SystemUI-managed alternative system bars."
Diffstat (limited to 'packages/SystemUI')
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java273
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java12
5 files changed, 406 insertions, 35 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 043b64c..f130993 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -25,17 +25,14 @@ import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
public class SystemUIService extends Service {
- static final String TAG = "SystemUIService";
+ private static final String TAG = "SystemUIService";
/**
- * The class names of the stuff to start.
+ * The classes of the stuff to start.
*/
- final Object[] SERVICES = new Object[] {
- R.string.config_statusBarComponent,
+ private final Class<?>[] SERVICES = new Class[] {
+ com.android.systemui.statusbar.SystemBars.class,
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
com.android.systemui.settings.SettingsUI.class,
@@ -44,29 +41,13 @@ public class SystemUIService extends Service {
/**
* Hold a reference on the stuff we start.
*/
- SystemUI[] mServices;
-
- private Class chooseClass(Object o) {
- if (o instanceof Integer) {
- final String cl = getString((Integer)o);
- try {
- return getClassLoader().loadClass(cl);
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException(ex);
- }
- } else if (o instanceof Class) {
- return (Class)o;
- } else {
- throw new RuntimeException("Unknown system ui service: " + o);
- }
- }
+ private final SystemUI[] mServices = new SystemUI[SERVICES.length];
@Override
public void onCreate() {
final int N = SERVICES.length;
- mServices = new SystemUI[N];
for (int i=0; i<N; i++) {
- Class cl = chooseClass(SERVICES[i]);
+ Class<?> cl = SERVICES[i];
Log.d(TAG, "loading: " + cl);
try {
mServices[i] = (SystemUI)cl.newInstance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 0879e0f..bf82466 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -194,6 +194,18 @@ public abstract class BaseStatusBar extends SystemUI implements
}
};
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+ userSwitched(mCurrentUserId);
+ }
+ }
+ };
+
public void start() {
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -271,16 +283,7 @@ public abstract class BaseStatusBar extends SystemUI implements
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
- userSwitched(mCurrentUserId);
- }
- }}, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
mLocale = mContext.getResources().getConfiguration().locale;
}
@@ -1177,4 +1180,11 @@ public abstract class BaseStatusBar extends SystemUI implements
public void resumeAutohide() {
// hook for subclasses
}
+
+ public void destroy() {
+ if (mSearchPanelView != null) {
+ mWindowManager.removeViewImmediate(mSearchPanelView);
+ }
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java
new file mode 100644
index 0000000..f763f03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2013 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.systemui.statusbar;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Manages a persistent connection to a service component defined in a secure setting.
+ *
+ * <p>If a valid service component is specified in the secure setting, starts it up and keeps it
+ * running; handling setting changes, package updates, component disabling, and unexpected
+ * process termination.
+ *
+ * <p>Clients can listen for important events using the supplied {@link Callbacks}.
+ */
+public class ServiceMonitor {
+ private static final int RECHECK_DELAY = 2000;
+ private static final int WAIT_FOR_STOP = 500;
+
+ public interface Callbacks {
+ /** The service does not exist or failed to bind */
+ void onNoService();
+ /** The service is about to start, this is a chance to perform cleanup and
+ * delay the start if necessary */
+ long onServiceStartAttempt();
+ }
+
+ // internal handler + messages used to serialize access to internal state
+ public static final int MSG_START_SERVICE = 1;
+ public static final int MSG_CONTINUE_START_SERVICE = 2;
+ public static final int MSG_STOP_SERVICE = 3;
+ public static final int MSG_PACKAGE_INTENT = 4;
+ public static final int MSG_CHECK_BOUND = 5;
+ public static final int MSG_SERVICE_DISCONNECTED = 6;
+
+ private final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_START_SERVICE:
+ startService();
+ break;
+ case MSG_CONTINUE_START_SERVICE:
+ continueStartService();
+ break;
+ case MSG_STOP_SERVICE:
+ stopService();
+ break;
+ case MSG_PACKAGE_INTENT:
+ packageIntent((Intent)msg.obj);
+ break;
+ case MSG_CHECK_BOUND:
+ checkBound();
+ break;
+ case MSG_SERVICE_DISCONNECTED:
+ serviceDisconnected((ComponentName)msg.obj);
+ break;
+ }
+ }
+ };
+
+ private final ContentObserver mSettingObserver = new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ onChange(selfChange, null);
+ }
+
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mDebug) Log.d(mTag, "onChange selfChange=" + selfChange + " uri=" + uri);
+ if (mBound) {
+ mHandler.sendEmptyMessage(MSG_STOP_SERVICE);
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_START_SERVICE, WAIT_FOR_STOP);
+ }
+ };
+
+ private final class SC implements ServiceConnection, IBinder.DeathRecipient {
+ private ComponentName mName;
+ private IBinder mService;
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mDebug) Log.d(mTag, "onServiceConnected name=" + name + " service=" + service);
+ mName = name;
+ mService = service;
+ try {
+ service.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Log.w(mTag, "Error linking to death", e);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (mDebug) Log.d(mTag, "onServiceDisconnected name=" + name);
+ boolean unlinked = mService.unlinkToDeath(this, 0);
+ if (mDebug) Log.d(mTag, " unlinked=" + unlinked);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, mName));
+ }
+
+ public void binderDied() {
+ if (mDebug) Log.d(mTag, "binderDied");
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, mName));
+ }
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String pkg = intent.getData().getSchemeSpecificPart();
+ if (mServiceName != null && mServiceName.getPackageName().equals(pkg)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_PACKAGE_INTENT, intent));
+ }
+ }
+ };
+
+ private final String mTag;
+ private final boolean mDebug;
+
+ private final Context mContext;
+ private final String mSettingKey;
+ private final Callbacks mCallbacks;
+
+ private ComponentName mServiceName;
+ private SC mServiceConnection;
+ private boolean mBound;
+
+ public ServiceMonitor(String ownerTag, boolean debug,
+ Context context, String settingKey, Callbacks callbacks) {
+ mTag = ownerTag + ".ServiceMonitor";
+ mDebug = debug;
+ mContext = context;
+ mSettingKey = settingKey;
+ mCallbacks = callbacks;
+ }
+
+ public void start() {
+ // listen for setting changes
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
+ false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);
+
+ // listen for package/component changes
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ mHandler.sendEmptyMessage(MSG_START_SERVICE);
+ }
+
+ // everything below is called on the handler
+
+ private void packageIntent(Intent intent) {
+ if (mDebug) Log.d(mTag, "packageIntent intent=" + intent
+ + " extras=" + bundleToString(intent.getExtras()));
+ if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
+ mHandler.sendEmptyMessage(MSG_START_SERVICE);
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
+ PackageManager pm = mContext.getPackageManager();
+ boolean serviceEnabled =
+ pm.getApplicationEnabledSetting(mServiceName.getPackageName())
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ && pm.getComponentEnabledSetting(mServiceName)
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ if (mBound && !serviceEnabled) {
+ stopService();
+ scheduleCheckBound();
+ } else if (!mBound && serviceEnabled) {
+ startService();
+ }
+ }
+ }
+
+ private void stopService() {
+ if (mDebug) Log.d(mTag, "stopService");
+ boolean stopped = mContext.stopService(new Intent().setComponent(mServiceName));
+ if (mDebug) Log.d(mTag, " stopped=" + stopped);
+ mContext.unbindService(mServiceConnection);
+ mBound = false;
+ }
+
+ private void startService() {
+ String cn = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ mSettingKey, UserHandle.USER_CURRENT);
+ mServiceName = cn == null ? null : ComponentName.unflattenFromString(cn);
+ if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName);
+ if (mServiceName == null) {
+ mBound = false;
+ mCallbacks.onNoService();
+ } else {
+ long delay = mCallbacks.onServiceStartAttempt();
+ mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);
+ }
+ }
+
+ private void continueStartService() {
+ if (mDebug) Log.d(mTag, "continueStartService");
+ Intent intent = new Intent().setComponent(mServiceName);
+ try {
+ mServiceConnection = new SC();
+ mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ if (mDebug) Log.d(mTag, "mBound: " + mBound);
+ } catch (Throwable t) {
+ Log.w(mTag, "Error binding to service: " + mServiceName, t);
+ }
+ if (!mBound) {
+ mCallbacks.onNoService();
+ }
+ }
+
+ private void serviceDisconnected(ComponentName serviceName) {
+ if (mDebug) Log.d(mTag, "serviceDisconnected serviceName=" + serviceName
+ + " mServiceName=" + mServiceName);
+ if (serviceName.equals(mServiceName)) {
+ mBound = false;
+ scheduleCheckBound();
+ }
+ }
+
+ private void checkBound() {
+ if (mDebug) Log.d(mTag, "checkBound mBound=" + mBound);
+ if (!mBound) {
+ startService();
+ }
+ }
+
+ private void scheduleCheckBound() {
+ mHandler.removeMessages(MSG_CHECK_BOUND);
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_BOUND, RECHECK_DELAY);
+ }
+
+ private static String bundleToString(Bundle bundle) {
+ if (bundle == null) return null;
+ StringBuilder sb = new StringBuilder('{');
+ for (String key : bundle.keySet()) {
+ if (sb.length() > 1) sb.append(',');
+ Object v = bundle.get(key);
+ v = (v instanceof String[]) ? Arrays.asList((String[]) v) : v;
+ sb.append(key).append('=').append(v);
+ }
+ return sb.append('}').toString();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
new file mode 100644
index 0000000..847bf96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2013 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.systemui.statusbar;
+
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+/**
+ * Ensure a single status bar service implementation is running at all times.
+ *
+ * <p>The implementation either comes from a service component running in a remote process (defined
+ * using a secure setting), else falls back to using the in-process implementation according
+ * to the product config.
+ */
+public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
+ private static final String TAG = "SystemBars";
+ private static final boolean DEBUG = true;
+ private static final int WAIT_FOR_BARS_TO_DIE = 500;
+
+ // manages the implementation coming from the remote process
+ private ServiceMonitor mServiceMonitor;
+
+ // in-process fallback implementation, per the product config
+ private BaseStatusBar mStatusBar;
+
+ @Override
+ public void start() {
+ if (DEBUG) Log.d(TAG, "start");
+ mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
+ mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
+ mServiceMonitor.start(); // will call onNoService if no remote service is found
+ }
+
+ @Override
+ public void onNoService() {
+ if (DEBUG) Log.d(TAG, "onNoService");
+ createStatusBarFromConfig(); // fallback to using an in-process implementation
+ }
+
+ @Override
+ public long onServiceStartAttempt() {
+ if (DEBUG) Log.d(TAG, "onServiceStartAttempt mStatusBar="+mStatusBar);
+ if (mStatusBar != null) {
+ // tear down the in-process version, we'll recreate it again if needed
+ mStatusBar.destroy();
+ mStatusBar = null;
+ return WAIT_FOR_BARS_TO_DIE;
+ }
+ return 0;
+ }
+
+ private void createStatusBarFromConfig() {
+ if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
+ final String clsName = mContext.getString(R.string.config_statusBarComponent);
+ if (clsName == null || clsName.length() == 0) {
+ throw andLog("No status bar component configured", null);
+ }
+ Class<?> cls = null;
+ try {
+ cls = mContext.getClassLoader().loadClass(clsName);
+ } catch (Throwable t) {
+ throw andLog("Error loading status bar component: " + clsName, t);
+ }
+ try {
+ mStatusBar = (BaseStatusBar) cls.newInstance();
+ } catch (Throwable t) {
+ throw andLog("Error creating status bar component: " + clsName, t);
+ }
+ mStatusBar.mContext = mContext;
+ mStatusBar.start();
+ if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
+ }
+
+ private RuntimeException andLog(String msg, Throwable t) {
+ Log.w(TAG, msg, t);
+ throw new RuntimeException(msg, t);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d86760f..dca8c97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2691,4 +2691,16 @@ public class PhoneStatusBar extends BaseStatusBar {
public void setBounds(Rect bounds) {
}
}
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ if (mStatusBarWindow != null) {
+ mWindowManager.removeViewImmediate(mStatusBarWindow);
+ }
+ if (mNavigationBarView != null) {
+ mWindowManager.removeViewImmediate(mNavigationBarView);
+ }
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
}