diff options
author | John Spurlock <jspurlock@google.com> | 2013-06-21 21:34:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-06-21 21:34:38 +0000 |
commit | e539ee865f06e9190dea38b7e277b096a608efd6 (patch) | |
tree | ac62ea18db40900b15493af9657e5a0e163d9be7 /packages/SystemUI | |
parent | 4a7052a7755bbcc332924ebcfaff8402c0613c7b (diff) | |
parent | 5c4541246c6a70f53552423dc35940386788bd5f (diff) | |
download | frameworks_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')
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); + } } |