Loading core/java/android/provider/Settings.java +3 −0 Original line number Diff line number Diff line Loading @@ -4089,6 +4089,9 @@ public final class Settings { */ public static final String DIALPAD_AUTOCOMPLETE = "dialpad_autocomplete"; /** @hide */ public static final String BAR_SERVICE_COMPONENT = "bar_service_component"; /** * This are the settings to be backed up. * Loading packages/SystemUI/src/com/android/systemui/SystemUIService.java +6 −25 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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(); Loading packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +20 −10 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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; } Loading Loading @@ -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); } } packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java 0 → 100644 +273 −0 Original line number Diff line number Diff line /* * 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(); } } packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java 0 → 100644 +95 −0 Original line number Diff line number Diff line /* * 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); } } Loading
core/java/android/provider/Settings.java +3 −0 Original line number Diff line number Diff line Loading @@ -4089,6 +4089,9 @@ public final class Settings { */ public static final String DIALPAD_AUTOCOMPLETE = "dialpad_autocomplete"; /** @hide */ public static final String BAR_SERVICE_COMPONENT = "bar_service_component"; /** * This are the settings to be backed up. * Loading
packages/SystemUI/src/com/android/systemui/SystemUIService.java +6 −25 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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(); Loading
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +20 −10 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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; } Loading Loading @@ -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); } }
packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java 0 → 100644 +273 −0 Original line number Diff line number Diff line /* * 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(); } }
packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java 0 → 100644 +95 −0 Original line number Diff line number Diff line /* * 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); } }