diff options
Diffstat (limited to 'services/core')
41 files changed, 1850 insertions, 1120 deletions
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java deleted file mode 100644 index 7249985..0000000 --- a/services/core/java/com/android/server/BootReceiver.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.IPackageManager; -import android.os.Build; -import android.os.DropBoxManager; -import android.os.FileObserver; -import android.os.FileUtils; -import android.os.RecoverySystem; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemProperties; -import android.provider.Downloads; -import android.util.Slog; - -import java.io.File; -import java.io.IOException; - -/** - * Performs a number of miscellaneous, non-system-critical actions - * after the system has finished booting. - */ -public class BootReceiver extends BroadcastReceiver { - private static final String TAG = "BootReceiver"; - - // Maximum size of a logged event (files get truncated if they're longer). - // Give userdebug builds a larger max to capture extra debug, esp. for last_kmsg. - private static final int LOG_SIZE = - SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536; - - private static final File TOMBSTONE_DIR = new File("/data/tombstones"); - - // The pre-froyo package and class of the system updater, which - // ran in the system process. We need to remove its packages here - // in order to clean up after a pre-froyo-to-froyo update. - private static final String OLD_UPDATER_PACKAGE = - "com.google.android.systemupdater"; - private static final String OLD_UPDATER_CLASS = - "com.google.android.systemupdater.SystemUpdateReceiver"; - - // Keep a reference to the observer so the finalizer doesn't disable it. - private static FileObserver sTombstoneObserver = null; - - @Override - public void onReceive(final Context context, Intent intent) { - // Log boot events in the background to avoid blocking the main thread with I/O - new Thread() { - @Override - public void run() { - try { - logBootEvents(context); - } catch (Exception e) { - Slog.e(TAG, "Can't log boot events", e); - } - try { - boolean onlyCore = false; - try { - onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService( - "package")).isOnlyCoreApps(); - } catch (RemoteException e) { - } - if (!onlyCore) { - removeOldUpdatePackages(context); - } - } catch (Exception e) { - Slog.e(TAG, "Can't remove old update packages", e); - } - - } - }.start(); - } - - private void removeOldUpdatePackages(Context context) { - Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS); - } - - private void logBootEvents(Context ctx) throws IOException { - final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE); - final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE); - final String headers = new StringBuilder(512) - .append("Build: ").append(Build.FINGERPRINT).append("\n") - .append("Hardware: ").append(Build.BOARD).append("\n") - .append("Revision: ") - .append(SystemProperties.get("ro.revision", "")).append("\n") - .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") - .append("Radio: ").append(Build.RADIO).append("\n") - .append("Kernel: ") - .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) - .append("\n").toString(); - final String bootReason = SystemProperties.get("ro.boot.bootreason", null); - - String recovery = RecoverySystem.handleAftermath(); - if (recovery != null && db != null) { - db.addText("SYSTEM_RECOVERY_LOG", headers + recovery); - } - - String lastKmsgFooter = ""; - if (bootReason != null) { - lastKmsgFooter = new StringBuilder(512) - .append("\n") - .append("Boot info:\n") - .append("Last boot reason: ").append(bootReason).append("\n") - .toString(); - } - - if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) { - String now = Long.toString(System.currentTimeMillis()); - SystemProperties.set("ro.runtime.firstboot", now); - if (db != null) db.addText("SYSTEM_BOOT", headers); - - // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) - addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter, - "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG"); - addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter, - "/sys/fs/pstore/console-ramoops", -LOG_SIZE, - "SYSTEM_LAST_KMSG"); - addFileToDropBox(db, prefs, headers, "/cache/recovery/log", - -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); - addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", - -LOG_SIZE, "APANIC_CONSOLE"); - addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", - -LOG_SIZE, "APANIC_THREADS"); - addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT"); - addFsckErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_FSCK"); - } else { - if (db != null) db.addText("SYSTEM_RESTART", headers); - } - - // Scan existing tombstones (in case any new ones appeared) - File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); - for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { - addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(), - LOG_SIZE, "SYSTEM_TOMBSTONE"); - } - - // Start watching for new tombstone files; will record them as they occur. - // This gets registered with the singleton file observer thread. - sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) { - @Override - public void onEvent(int event, String path) { - try { - String filename = new File(TOMBSTONE_DIR, path).getPath(); - addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE"); - } catch (IOException e) { - Slog.e(TAG, "Can't log tombstone", e); - } - } - }; - - sTombstoneObserver.startWatching(); - } - - private static void addFileToDropBox( - DropBoxManager db, SharedPreferences prefs, - String headers, String filename, int maxSize, String tag) throws IOException { - addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize, - tag); - } - - private static void addFileWithFootersToDropBox( - DropBoxManager db, SharedPreferences prefs, - String headers, String footers, String filename, int maxSize, - String tag) throws IOException { - if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled - - File file = new File(filename); - long fileTime = file.lastModified(); - if (fileTime <= 0) return; // File does not exist - - if (prefs != null) { - long lastTime = prefs.getLong(filename, 0); - if (lastTime == fileTime) return; // Already logged this particular file - // TODO: move all these SharedPreferences Editor commits - // outside this function to the end of logBootEvents - prefs.edit().putLong(filename, fileTime).apply(); - } - - Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); - db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers); - } - - private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs, - String headers, int maxSize, String tag) throws IOException { - if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled - Slog.i(TAG, "Copying audit failures to DropBox"); - - File file = new File("/proc/last_kmsg"); - long fileTime = file.lastModified(); - if (fileTime <= 0) { - file = new File("/sys/fs/pstore/console-ramoops"); - fileTime = file.lastModified(); - } - - if (fileTime <= 0) return; // File does not exist - - if (prefs != null) { - long lastTime = prefs.getLong(tag, 0); - if (lastTime == fileTime) return; // Already logged this particular file - // TODO: move all these SharedPreferences Editor commits - // outside this function to the end of logBootEvents - prefs.edit().putLong(tag, fileTime).apply(); - } - - String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); - StringBuilder sb = new StringBuilder(); - for (String line : log.split("\n")) { - if (line.contains("audit")) { - sb.append(line + "\n"); - } - } - Slog.i(TAG, "Copied " + sb.toString().length() + " worth of audits to DropBox"); - db.addText(tag, headers + sb.toString()); - } - - private static void addFsckErrorsToDropBox(DropBoxManager db, SharedPreferences prefs, - String headers, int maxSize, String tag) throws IOException { - boolean upload_needed = false; - if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled - Slog.i(TAG, "Checking for fsck errors"); - - File file = new File("/dev/fscklogs/log"); - long fileTime = file.lastModified(); - if (fileTime <= 0) return; // File does not exist - - String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); - StringBuilder sb = new StringBuilder(); - for (String line : log.split("\n")) { - if (line.contains("FILE SYSTEM WAS MODIFIED")) { - upload_needed = true; - break; - } - } - - if (upload_needed) { - addFileToDropBox(db, prefs, headers, "/dev/fscklogs/log", maxSize, tag); - } - - // Remove the file so we don't re-upload if the runtime restarts. - file.delete(); - } -} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index dd5a7ea..5bef4bf 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -264,6 +264,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private INetworkManagementService mNetd; private INetworkPolicyManager mPolicyManager; + private String mCurrentTcpBufferSizes; + private static final int ENABLED = 1; private static final int DISABLED = 0; @@ -1534,11 +1536,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } - if (mtu < 68 || mtu > 10000) { + if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address()) == false) { loge("Unexpected mtu value: " + mtu + ", " + iface); return; } + // Cannot set MTU without interface name + if (TextUtils.isEmpty(iface)) { + loge("Setting MTU size with null iface."); + return; + } + try { if (VDBG) log("Setting MTU size: " + iface + ", " + mtu); mNetd.setMtu(iface, mtu); @@ -1547,30 +1555,40 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - /** - * Reads the network specific TCP buffer sizes from SystemProperties - * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system - * wide use - */ - private void updateNetworkSettings(NetworkStateTracker nt) { - String key = nt.getTcpBufferSizesPropName(); - String bufferSizes = key == null ? null : SystemProperties.get(key); + private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208"; + + private void updateTcpBufferSizes(NetworkAgentInfo nai) { + if (isDefaultNetwork(nai) == false) { + return; + } - if (TextUtils.isEmpty(bufferSizes)) { - if (VDBG) log(key + " not found in system properties. Using defaults"); + String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes(); + String[] values = null; + if (tcpBufferSizes != null) { + values = tcpBufferSizes.split(","); + } - // Setting to default values so we won't be stuck to previous values - key = "net.tcp.buffersize.default"; - bufferSizes = SystemProperties.get(key); + if (values == null || values.length != 6) { + if (VDBG) log("Invalid tcpBufferSizes string: " + tcpBufferSizes +", using defaults"); + tcpBufferSizes = DEFAULT_TCP_BUFFER_SIZES; + values = tcpBufferSizes.split(","); } - // Set values in kernel - if (bufferSizes.length() != 0) { - if (VDBG) { - log("Setting TCP values: [" + bufferSizes - + "] which comes from [" + key + "]"); - } - setBufferSize(bufferSizes); + if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return; + + try { + if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes); + + final String prefix = "/sys/kernel/ipv4/tcp_"; + FileUtils.stringToFile(prefix + "rmem_min", values[0]); + FileUtils.stringToFile(prefix + "rmem_def", values[1]); + FileUtils.stringToFile(prefix + "rmem_max", values[2]); + FileUtils.stringToFile(prefix + "wmem_min", values[3]); + FileUtils.stringToFile(prefix + "wmem_def", values[4]); + FileUtils.stringToFile(prefix + "wmem_max", values[5]); + mCurrentTcpBufferSizes = tcpBufferSizes; + } catch (IOException e) { + loge("Can't set TCP buffer sizes:" + e); } final String defaultRwndKey = "net.tcp.default_init_rwnd"; @@ -1583,33 +1601,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - /** - * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max] - * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem - * - * @param bufferSizes in the format of "readMin, readInitial, readMax, - * writeMin, writeInitial, writeMax" - */ - private void setBufferSize(String bufferSizes) { - try { - String[] values = bufferSizes.split(","); - - if (values.length == 6) { - final String prefix = "/sys/kernel/ipv4/tcp_"; - FileUtils.stringToFile(prefix + "rmem_min", values[0]); - FileUtils.stringToFile(prefix + "rmem_def", values[1]); - FileUtils.stringToFile(prefix + "rmem_max", values[2]); - FileUtils.stringToFile(prefix + "wmem_min", values[3]); - FileUtils.stringToFile(prefix + "wmem_def", values[4]); - FileUtils.stringToFile(prefix + "wmem_max", values[5]); - } else { - loge("Invalid buffersize string: " + bufferSizes); - } - } catch (IOException e) { - loge("Can't set tcp buffer sizes:" + e); - } - } - private void flushVmDnsCache() { /* * Tell the VMs to toss their DNS caches @@ -1972,12 +1963,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ break; } - case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: { - info = (NetworkInfo) msg.obj; - int type = info.getType(); - if (mNetConfigs[type].isDefault()) updateNetworkSettings(mNetTrackers[type]); - break; - } } } } @@ -4146,6 +4131,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { updateInterfaces(newLp, oldLp, netId); updateMtu(newLp, oldLp); + updateTcpBufferSizes(networkAgent); // TODO - figure out what to do for clat // for (LinkProperties lp : newLp.getStackedLinks()) { // updateMtu(lp, null); @@ -4371,6 +4357,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { loge("Exception setting default network :" + e); } handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy()); + updateTcpBufferSizes(newNetwork); } private void handleConnectionValidated(NetworkAgentInfo newNetwork) { @@ -4428,6 +4415,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { isNewDefault = true; updateActiveDefaultNetwork(newNetwork); if (newNetwork.linkProperties != null) { + updateTcpBufferSizes(newNetwork); setDefaultDnsSystemProperties( newNetwork.linkProperties.getDnsServers()); } else { @@ -4486,8 +4474,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { 1000); } } - // TODO - read the tcp buffer size config string from somewhere - // updateNetworkSettings(); } // Notify battery stats service about this network, both the normal diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 4687e3f..e0f9b9c 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -603,10 +603,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0, enabled ? 1 : 0)); } - public void handleHardKeyboardStatusChange(boolean available, boolean enabled) { + public void handleHardKeyboardStatusChange(boolean available, + boolean showImeWithHardKeyboard) { if (DEBUG) { - Slog.w(TAG, "HardKeyboardStatusChanged: available = " + available + ", enabled = " - + enabled); + Slog.w(TAG, "HardKeyboardStatusChanged: available = " + available + + ", showImeWithHardKeyboard= " + showImeWithHardKeyboard); } synchronized(mMethodMap) { if (mSwitchingDialog != null && mSwitchingDialogTitleView != null @@ -2810,11 +2811,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub ? View.VISIBLE : View.GONE); final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById( com.android.internal.R.id.hard_keyboard_switch); - hardKeySwitch.setChecked(mWindowManagerService.isHardKeyboardEnabled()); + hardKeySwitch.setChecked(mWindowManagerService.isShowImeWithHardKeyboardEnabled()); hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mWindowManagerService.setHardKeyboardEnabled(isChecked); + mWindowManagerService.setShowImeWithHardKeyboard(isChecked); // Ensure that the input method dialog is dismissed when changing // the hardware keyboard state. hideInputMethodMenu(); diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 64b0487..07cc864 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -69,6 +69,124 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { } } + private boolean filterEquals(IntentFilter f1, IntentFilter f2) { + int s1 = f1.countActions(); + int s2 = f2.countActions(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasAction(f1.getAction(i))) { + return false; + } + } + s1 = f1.countCategories(); + s2 = f2.countCategories(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasCategory(f1.getCategory(i))) { + return false; + } + } + s1 = f1.countDataTypes(); + s2 = f2.countDataTypes(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasExactDataType(f1.getDataType(i))) { + return false; + } + } + s1 = f1.countDataSchemes(); + s2 = f2.countDataSchemes(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasDataScheme(f1.getDataScheme(i))) { + return false; + } + } + s1 = f1.countDataAuthorities(); + s2 = f2.countDataAuthorities(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasDataAuthority(f1.getDataAuthority(i))) { + return false; + } + } + s1 = f1.countDataPaths(); + s2 = f2.countDataPaths(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasDataPath(f1.getDataPath(i))) { + return false; + } + } + s1 = f1.countDataSchemeSpecificParts(); + s2 = f2.countDataSchemeSpecificParts(); + if (s1 != s2) { + return false; + } + for (int i=0; i<s1; i++) { + if (!f2.hasDataSchemeSpecificPart(f1.getDataSchemeSpecificPart(i))) { + return false; + } + } + return true; + } + + private ArrayList<F> collectFilters(F[] array, IntentFilter matching) { + ArrayList<F> res = null; + if (array != null) { + for (int i=0; i<array.length; i++) { + F cur = array[i]; + if (cur == null) { + break; + } + if (filterEquals(cur, matching)) { + if (res == null) { + res = new ArrayList<>(); + } + res.add(cur); + } + } + } + return res; + } + + public ArrayList<F> findFilters(IntentFilter matching) { + if (matching.countDataSchemes() == 1) { + // Fast case. + return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching); + } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) { + // Another fast case. + return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching); + } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0 + && matching.countActions() == 1) { + // Last fast case. + return collectFilters(mActionToFilter.get(matching.getAction(0)), matching); + } else { + ArrayList<F> res = null; + for (F cur : mFilters) { + if (filterEquals(cur, matching)) { + if (res == null) { + res = new ArrayList<>(); + } + res.add(cur); + } + } + return res; + } + } + public void removeFilter(F f) { removeFilterInternal(f); mFilters.remove(f); diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index ab4d4dc..395e365 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -17,9 +17,9 @@ package com.android.server; import android.Manifest.permission; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.INetworkScoreCache; import android.net.INetworkScoreService; @@ -30,6 +30,7 @@ import android.net.ScoredNetwork; import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -51,9 +52,6 @@ import java.util.Set; public class NetworkScoreService extends INetworkScoreService.Stub { private static final String TAG = "NetworkScoreService"; - /** SharedPreference bit set to true after the service is first initialized. */ - private static final String PREF_SCORING_PROVISIONED = "is_provisioned"; - private final Context mContext; private final Map<Integer, INetworkScoreCache> mScoreCaches; @@ -65,8 +63,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Called when the system is ready to run third-party code but before it actually does so. */ void systemReady() { - SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE); - if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) { + ContentResolver cr = mContext.getContentResolver(); + if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) { // On first run, we try to initialize the scorer to the one configured at build time. // This will be a no-op if the scorer isn't actually valid. String defaultPackage = mContext.getResources().getString( @@ -74,7 +72,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { if (!TextUtils.isEmpty(defaultPackage)) { NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage); } - prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply(); + Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1); } } diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java new file mode 100644 index 0000000..6e67970 --- /dev/null +++ b/services/core/java/com/android/server/SystemService.java @@ -0,0 +1,199 @@ +/* + * 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.server; + +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * The base class for services running in the system process. Override and implement + * the lifecycle event callback methods as needed. + * <p> + * The lifecycle of a SystemService: + * </p><ul> + * <li>The constructor is called and provided with the system {@link Context} + * to initialize the system service. + * <li>{@link #onStart()} is called to get the service running. The service should + * publish its binder interface at this point using + * {@link #publishBinderService(String, IBinder)}. It may also publish additional + * local interfaces that other services within the system server may use to access + * privileged internal functions. + * <li>Then {@link #onBootPhase(int)} is called as many times as there are boot phases + * until {@link #PHASE_BOOT_COMPLETE} is sent, which is the last boot phase. Each phase + * is an opportunity to do special work, like acquiring optional service dependencies, + * waiting to see if SafeMode is enabled, or registering with a service that gets + * started after this one. + * </ul><p> + * NOTE: All lifecycle methods are called from the system server's main looper thread. + * </p> + * + * {@hide} + */ +public abstract class SystemService { + /* + * Boot Phases + */ + public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency? + + /** + * After receiving this boot phase, services can obtain lock settings data. + */ + public static final int PHASE_LOCK_SETTINGS_READY = 480; + + /** + * After receiving this boot phase, services can safely call into core system services + * such as the PowerManager or PackageManager. + */ + public static final int PHASE_SYSTEM_SERVICES_READY = 500; + + /** + * After receiving this boot phase, services can broadcast Intents. + */ + public static final int PHASE_ACTIVITY_MANAGER_READY = 550; + + /** + * After receiving this boot phase, services can start/bind to third party apps. + * Apps will be able to make Binder calls into services at this point. + */ + public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; + + /** + * After receiving this boot phase, services can allow user interaction with the device. + * This phase occurs when boot has completed and the home application has started. + * System services may prefer to listen to this phase rather than registering a + * broadcast receiver for ACTION_BOOT_COMPLETED to reduce overall latency. + */ + public static final int PHASE_BOOT_COMPLETED = 1000; + + private final Context mContext; + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public SystemService(Context context) { + mContext = context; + } + + /** + * Gets the system context. + */ + public final Context getContext() { + return mContext; + } + + /** + * Returns true if the system is running in safe mode. + * TODO: we should define in which phase this becomes valid + */ + public final boolean isSafeMode() { + return getManager().isSafeMode(); + } + + /** + * Called when the dependencies listed in the @Service class-annotation are available + * and after the chosen start phase. + * When this method returns, the service should be published. + */ + public abstract void onStart(); + + /** + * Called on each phase of the boot process. Phases before the service's start phase + * (as defined in the @Service annotation) are never received. + * + * @param phase The current boot phase. + */ + public void onBootPhase(int phase) {} + + /** + * Called when a new user is starting, for system services to initialize any per-user + * state they maintain for running users. + * @param userHandle The identifier of the user. + */ + public void onStartUser(int userHandle) {} + + /** + * Called when switching to a different foreground user, for system services that have + * special behavior for whichever user is currently in the foreground. This is called + * before any application processes are aware of the new user. + * @param userHandle The identifier of the user. + */ + public void onSwitchUser(int userHandle) {} + + /** + * Called when an existing user is stopping, for system services to finalize any per-user + * state they maintain for running users. This is called prior to sending the SHUTDOWN + * broadcast to the user; it is a good place to stop making use of any resources of that + * user (such as binding to a service running in the user). + * @param userHandle The identifier of the user. + */ + public void onStopUser(int userHandle) {} + + /** + * Called when an existing user is stopping, for system services to finalize any per-user + * state they maintain for running users. This is called after all application process + * teardown of the user is complete. + * @param userHandle The identifier of the user. + */ + public void onCleanupUser(int userHandle) {} + + /** + * Publish the service so it is accessible to other services and apps. + */ + protected final void publishBinderService(String name, IBinder service) { + publishBinderService(name, service, false); + } + + /** + * Publish the service so it is accessible to other services and apps. + */ + protected final void publishBinderService(String name, IBinder service, + boolean allowIsolated) { + ServiceManager.addService(name, service, allowIsolated); + } + + /** + * Get a binder service by its name. + */ + protected final IBinder getBinderService(String name) { + return ServiceManager.getService(name); + } + + /** + * Publish the service so it is only accessible to the system process. + */ + protected final <T> void publishLocalService(Class<T> type, T service) { + LocalServices.addService(type, service); + } + + /** + * Get a local service by interface. + */ + protected final <T> T getLocalService(Class<T> type) { + return LocalServices.getService(type); + } + + private SystemServiceManager getManager() { + return LocalServices.getService(SystemServiceManager.class); + } +} diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java new file mode 100644 index 0000000..fda6479 --- /dev/null +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -0,0 +1,227 @@ +/* + * 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.server; + +import android.content.Context; +import android.util.Slog; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +/** + * Manages creating, starting, and other lifecycle events of + * {@link com.android.server.SystemService system services}. + * + * {@hide} + */ +public class SystemServiceManager { + private static final String TAG = "SystemServiceManager"; + + private final Context mContext; + private boolean mSafeMode; + + // Services that should receive lifecycle events. + private final ArrayList<SystemService> mServices = new ArrayList<SystemService>(); + + private int mCurrentPhase = -1; + + public SystemServiceManager(Context context) { + mContext = context; + } + + /** + * Starts a service by class name. + * + * @return The service instance. + */ + @SuppressWarnings("unchecked") + public SystemService startService(String className) { + final Class<SystemService> serviceClass; + try { + serviceClass = (Class<SystemService>)Class.forName(className); + } catch (ClassNotFoundException ex) { + Slog.i(TAG, "Starting " + className); + throw new RuntimeException("Failed to create service " + className + + ": service class not found, usually indicates that the caller should " + + "have called PackageManager.hasSystemFeature() to check whether the " + + "feature is available on this device before trying to start the " + + "services that implement it", ex); + } + return startService(serviceClass); + } + + /** + * Creates and starts a system service. The class must be a subclass of + * {@link com.android.server.SystemService}. + * + * @param serviceClass A Java class that implements the SystemService interface. + * @return The service instance, never null. + * @throws RuntimeException if the service fails to start. + */ + @SuppressWarnings("unchecked") + public <T extends SystemService> T startService(Class<T> serviceClass) { + final String name = serviceClass.getName(); + Slog.i(TAG, "Starting " + name); + + // Create the service. + if (!SystemService.class.isAssignableFrom(serviceClass)) { + throw new RuntimeException("Failed to create " + name + + ": service must extend " + SystemService.class.getName()); + } + final T service; + try { + Constructor<T> constructor = serviceClass.getConstructor(Context.class); + service = constructor.newInstance(mContext); + } catch (InstantiationException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service could not be instantiated", ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service must have a public constructor with a Context argument", ex); + } catch (NoSuchMethodException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service must have a public constructor with a Context argument", ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service constructor threw an exception", ex); + } + + // Register it. + mServices.add(service); + + // Start it. + try { + service.onStart(); + } catch (RuntimeException ex) { + throw new RuntimeException("Failed to start service " + name + + ": onStart threw an exception", ex); + } + return service; + } + + /** + * Starts the specified boot phase for all system services that have been started up to + * this point. + * + * @param phase The boot phase to start. + */ + public void startBootPhase(final int phase) { + if (phase <= mCurrentPhase) { + throw new IllegalArgumentException("Next phase must be larger than previous"); + } + mCurrentPhase = phase; + + Slog.i(TAG, "Starting phase " + mCurrentPhase); + + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onBootPhase(mCurrentPhase); + } catch (Exception ex) { + throw new RuntimeException("Failed to boot service " + + service.getClass().getName() + + ": onBootPhase threw an exception during phase " + + mCurrentPhase, ex); + } + } + } + + public void startUser(final int userHandle) { + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onStartUser(userHandle); + } catch (Exception ex) { + Slog.wtf(TAG, "Failure reporting start of user " + userHandle + + " to service " + service.getClass().getName(), ex); + } + } + } + + public void switchUser(final int userHandle) { + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onSwitchUser(userHandle); + } catch (Exception ex) { + Slog.wtf(TAG, "Failure reporting switch of user " + userHandle + + " to service " + service.getClass().getName(), ex); + } + } + } + + public void stopUser(final int userHandle) { + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onStopUser(userHandle); + } catch (Exception ex) { + Slog.wtf(TAG, "Failure reporting stop of user " + userHandle + + " to service " + service.getClass().getName(), ex); + } + } + } + + public void cleanupUser(final int userHandle) { + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onCleanupUser(userHandle); + } catch (Exception ex) { + Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle + + " to service " + service.getClass().getName(), ex); + } + } + } + + /** Sets the safe mode flag for services to query. */ + public void setSafeMode(boolean safeMode) { + mSafeMode = safeMode; + } + + /** + * Returns whether we are booting into safe mode. + * @return safe mode flag + */ + public boolean isSafeMode() { + return mSafeMode; + } + + /** + * Outputs the state of this manager to the System log. + */ + public void dump() { + StringBuilder builder = new StringBuilder(); + builder.append("Current phase: ").append(mCurrentPhase).append("\n"); + builder.append("Services:\n"); + final int startedLen = mServices.size(); + for (int i = 0; i < startedLen; i++) { + final SystemService service = mServices.get(i); + builder.append("\t") + .append(service.getClass().getSimpleName()) + .append("\n"); + } + + Slog.e(TAG, builder.toString()); + } +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ad2704a..ecd8f11 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2031,7 +2031,7 @@ public final class ActivityManagerService extends ActivityManagerNative ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( "android", STOCK_PM_FLAGS); - mSystemThread.installSystemApplicationInfo(info); + mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader()); synchronized (this) { ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9a67321..46cb6c3 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -37,7 +37,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; -import android.text.format.DateUtils; +import android.os.Trace; import android.util.MathUtils; import android.util.Slog; import android.util.Spline; @@ -74,6 +74,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static boolean DEBUG = false; private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false; + private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked"; + // If true, uses the electron beam on animation. // We might want to turn this off if we cannot get a guarantee that the screen // actually turns on and starts showing new content after the call to set the @@ -714,11 +716,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void blockScreenOn() { if (!mScreenOnWasBlocked) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); mScreenOnWasBlocked = true; mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime(); - if (DEBUG) { - Slog.d(TAG, "Blocked screen on."); - } + Slog.i(TAG, "Blocking screen on until initial contents have been drawn."); } } @@ -726,9 +727,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mScreenOnWasBlocked) { mScreenOnWasBlocked = false; long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime; - if (delay > 1000 || DEBUG) { - Slog.d(TAG, "Unblocked screen on after " + delay + " ms"); - } + Slog.i(TAG, "Unblocked screen on after " + delay + " ms"); + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); } } diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 6522b89..a7651e4 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -23,6 +23,7 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; +import android.os.Trace; import android.util.FloatProperty; import android.util.IntProperty; import android.util.Slog; @@ -405,19 +406,38 @@ final class DisplayPowerState { } boolean suspending = Display.isSuspendedState(state); if (stateChanged && !suspending) { - mBlanker.requestDisplayState(state); + requestDisplayState(state); } if (backlightChanged) { - mBacklight.setBrightness(backlight); + setBrightness(backlight); } if (stateChanged && suspending) { - mBlanker.requestDisplayState(state); + requestDisplayState(state); } } // Let the outer class know that all changes have been applied. postScreenUpdateThreadSafe(); } + + private void requestDisplayState(int state) { + Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" + + Display.stateToString(state) + ")"); + try { + mBlanker.requestDisplayState(state); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } + } + + private void setBrightness(int backlight) { + Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")"); + try { + mBacklight.setBrightness(backlight); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } + } }; } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 4fd006d..9c91ab5 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -21,7 +21,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.SystemProperties; -import android.util.Pair; +import android.os.Trace; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -224,8 +224,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public void requestDisplayStateLocked(int state) { if (mState != state) { - SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(), - getPowerModeForState(state)); + final int mode = getPowerModeForState(state); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" + + Display.stateToString(state) + ", id=" + mBuiltInDisplayId + ")"); + try { + SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(), mode); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } mState = state; updateDeviceInfoLocked(); } diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 334f0ac..768ccf2 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.IBinder.DeathRecipient; +import android.os.Trace; import android.os.UserHandle; import android.service.dreams.DreamService; import android.service.dreams.IDreamService; @@ -111,41 +112,46 @@ final class DreamController { boolean isTest, boolean canDoze, int userId) { stopDream(true /*immediate*/); - // Close the notification shade. Don't need to send to all, but better to be explicit. - mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream"); + try { + // Close the notification shade. Don't need to send to all, but better to be explicit. + mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); - Slog.i(TAG, "Starting dream: name=" + name - + ", isTest=" + isTest + ", canDoze=" + canDoze - + ", userId=" + userId); + Slog.i(TAG, "Starting dream: name=" + name + + ", isTest=" + isTest + ", canDoze=" + canDoze + + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); + mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); - try { - mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM); - } catch (RemoteException ex) { - Slog.e(TAG, "Unable to add window token for dream.", ex); - stopDream(true /*immediate*/); - return; - } + try { + mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM); + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to add window token for dream.", ex); + stopDream(true /*immediate*/); + return; + } - Intent intent = new Intent(DreamService.SERVICE_INTERFACE); - intent.setComponent(name); - intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - try { - if (!mContext.bindServiceAsUser(intent, mCurrentDream, - Context.BIND_AUTO_CREATE, new UserHandle(userId))) { - Slog.e(TAG, "Unable to bind dream service: " + intent); + Intent intent = new Intent(DreamService.SERVICE_INTERFACE); + intent.setComponent(name); + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + try { + if (!mContext.bindServiceAsUser(intent, mCurrentDream, + Context.BIND_AUTO_CREATE, new UserHandle(userId))) { + Slog.e(TAG, "Unable to bind dream service: " + intent); + stopDream(true /*immediate*/); + return; + } + } catch (SecurityException ex) { + Slog.e(TAG, "Unable to bind dream service: " + intent, ex); stopDream(true /*immediate*/); return; } - } catch (SecurityException ex) { - Slog.e(TAG, "Unable to bind dream service: " + intent, ex); - stopDream(true /*immediate*/); - return; - } - mCurrentDream.mBound = true; - mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT); + mCurrentDream.mBound = true; + mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } } public void stopDream(boolean immediate) { @@ -153,71 +159,76 @@ final class DreamController { return; } - if (!immediate) { - if (mCurrentDream.mWakingGently) { - return; // already waking gently - } + Trace.traceBegin(Trace.TRACE_TAG_POWER, "stopDream"); + try { + if (!immediate) { + if (mCurrentDream.mWakingGently) { + return; // already waking gently + } - if (mCurrentDream.mService != null) { - // Give the dream a moment to wake up and finish itself gently. - mCurrentDream.mWakingGently = true; - try { - mCurrentDream.mService.wakeUp(); - mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT); - return; - } catch (RemoteException ex) { - // oh well, we tried, finish immediately instead + if (mCurrentDream.mService != null) { + // Give the dream a moment to wake up and finish itself gently. + mCurrentDream.mWakingGently = true; + try { + mCurrentDream.mService.wakeUp(); + mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT); + return; + } catch (RemoteException ex) { + // oh well, we tried, finish immediately instead + } } } - } - final DreamRecord oldDream = mCurrentDream; - mCurrentDream = null; - Slog.i(TAG, "Stopping dream: name=" + oldDream.mName - + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze - + ", userId=" + oldDream.mUserId); + final DreamRecord oldDream = mCurrentDream; + mCurrentDream = null; + Slog.i(TAG, "Stopping dream: name=" + oldDream.mName + + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze + + ", userId=" + oldDream.mUserId); - mHandler.removeCallbacks(mStopUnconnectedDreamRunnable); - mHandler.removeCallbacks(mStopStubbornDreamRunnable); + mHandler.removeCallbacks(mStopUnconnectedDreamRunnable); + mHandler.removeCallbacks(mStopStubbornDreamRunnable); - if (oldDream.mSentStartBroadcast) { - mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL); - } + if (oldDream.mSentStartBroadcast) { + mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL); + } - if (oldDream.mService != null) { - // Tell the dream that it's being stopped so that - // it can shut down nicely before we yank its window token out from - // under it. - try { - oldDream.mService.detach(); - } catch (RemoteException ex) { - // we don't care; this thing is on the way out + if (oldDream.mService != null) { + // Tell the dream that it's being stopped so that + // it can shut down nicely before we yank its window token out from + // under it. + try { + oldDream.mService.detach(); + } catch (RemoteException ex) { + // we don't care; this thing is on the way out + } + + try { + oldDream.mService.asBinder().unlinkToDeath(oldDream, 0); + } catch (NoSuchElementException ex) { + // don't care + } + oldDream.mService = null; } - try { - oldDream.mService.asBinder().unlinkToDeath(oldDream, 0); - } catch (NoSuchElementException ex) { - // don't care + if (oldDream.mBound) { + mContext.unbindService(oldDream); } - oldDream.mService = null; - } - if (oldDream.mBound) { - mContext.unbindService(oldDream); - } + try { + mIWindowManager.removeWindowToken(oldDream.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Error removing window token for dream.", ex); + } - try { - mIWindowManager.removeWindowToken(oldDream.mToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Error removing window token for dream.", ex); + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onDreamStopped(oldDream.mToken); + } + }); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } - - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDreamStopped(oldDream.mToken); - } - }); } private void attach(IDreamService service) { diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 2407253..4b812cf 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -173,6 +173,10 @@ final class Constants { static final int SYSTEM_AUDIO_STATUS_OFF = 0; static final int SYSTEM_AUDIO_STATUS_ON = 1; + // [Menu State] + static final int MENU_STATE_ACTIVATED = 0; + static final int MENU_STATE_DEACTIVATED = 1; + // Bit mask used to get the routing path of the top level device. // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0. static final int ROUTING_PATH_TOP_MASK = 0xF000; @@ -276,5 +280,10 @@ final class Constants { // values which denotes the device type in HDMI Spec 1.4. static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type"; + // MHL RCPE messages + static final int MHL_RCPE_NO_ERROR = 0x00; + static final int MHL_RCPE_INEFFECTIVE_KEYCODE = 0x01; + static final int MHL_RCPE_RESPONDER_BUSY = 0x02; + private Constants() { /* cannot be instantiated */ } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java index c0c8424..46b2b3e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java @@ -21,7 +21,7 @@ import android.view.KeyEvent; /** * Helper class to translate android keycode to hdmi cec keycode and vice versa. */ -public class HdmiCecKeycode { +final class HdmiCecKeycode { public static final int UNSUPPORTED_KEYCODE = -1; public static final int NO_PARAM = -1; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index b894fd7..4862f93 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -17,10 +17,15 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemClock; import android.util.Slog; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; @@ -39,15 +44,21 @@ abstract class HdmiCecLocalDevice { private static final String TAG = "HdmiCecLocalDevice"; private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1; + private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2; // Timeout in millisecond for device clean up (5s). // Normal actions timeout is 2s but some of them would have several sequence of timeout. private static final int DEVICE_CLEANUP_TIMEOUT = 5000; + // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior. + // When it expires, we can assume <User Control Release> is received. + private static final int FOLLOWER_SAFETY_TIMEOUT = 550; protected final HdmiControlService mService; protected final int mDeviceType; protected int mAddress; protected int mPreferredAddress; protected HdmiDeviceInfo mDeviceInfo; + protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; + protected int mLastKeyRepeatCount = 0; static class ActiveSource { int logicalAddress; @@ -111,6 +122,9 @@ abstract class HdmiCecLocalDevice { case MSG_DISABLE_DEVICE_TIMEOUT: handleDisableDeviceTimeout(); break; + case MSG_USER_CONTROL_RELEASE_TIMEOUT: + handleUserControlReleased(); + break; } } }; @@ -230,10 +244,14 @@ abstract class HdmiCecLocalDevice { return handleImageViewOn(message); case Constants.MESSAGE_USER_CONTROL_PRESSED: return handleUserControlPressed(message); + case Constants.MESSAGE_USER_CONTROL_RELEASED: + return handleUserControlReleased(); case Constants.MESSAGE_SET_STREAM_PATH: return handleSetStreamPath(message); case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS: return handleGiveDevicePowerStatus(message); + case Constants.MESSAGE_MENU_REQUEST: + return handleGiveDeviceMenuStatus(message); case Constants.MESSAGE_VENDOR_COMMAND: return handleVendorCommand(message); case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID: @@ -376,6 +394,7 @@ abstract class HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleUserControlPressed(HdmiCecMessage message) { assertRunOnServiceThread(); + mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT); if (mService.isPowerOnOrTransient() && isPowerOffOrToggleCommand(message)) { mService.standby(); return true; @@ -383,9 +402,54 @@ abstract class HdmiCecLocalDevice { mService.wakeUp(); return true; } + + final long downTime = SystemClock.uptimeMillis(); + final byte[] params = message.getParams(); + final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0], + params.length > 1 ? params[1] : HdmiCecKeycode.NO_PARAM); + int keyRepeatCount = 0; + if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { + if (keycode == mLastKeycode) { + keyRepeatCount = mLastKeyRepeatCount + 1; + } else { + injectKeyEvent(downTime, KeyEvent.ACTION_UP, mLastKeycode, 0); + } + } + mLastKeycode = keycode; + mLastKeyRepeatCount = keyRepeatCount; + + if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { + injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount); + mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT), + FOLLOWER_SAFETY_TIMEOUT); + return true; + } + return false; + } + + @ServiceThreadOnly + protected boolean handleUserControlReleased() { + assertRunOnServiceThread(); + mHandler.removeMessages(MSG_USER_CONTROL_RELEASE_TIMEOUT); + mLastKeyRepeatCount = 0; + if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { + final long upTime = SystemClock.uptimeMillis(); + injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0); + mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; + return true; + } return false; } + static void injectKeyEvent(long time, int action, int keycode, int repeat) { + KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode, + repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, + InputDevice.SOURCE_HDMI, null); + InputManager.getInstance().injectInputEvent(keyEvent, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + keyEvent.recycle(); + } + static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) { byte[] params = message.getParams(); return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED @@ -420,6 +484,13 @@ abstract class HdmiCecLocalDevice { return true; } + protected boolean handleGiveDeviceMenuStatus(HdmiCecMessage message) { + // Always report menu active to receive Remote Control. + mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( + mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED)); + return true; + } + protected boolean handleVendorCommand(HdmiCecMessage message) { mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), message.getParams(), false); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 47b0794..809fef4 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -100,7 +100,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private List<HdmiDeviceInfo> mSafeExternalInputs = Collections.emptyList(); // Map-like container of all cec devices including local ones. - // A logical address of device is used as key of container. + // device id is used as key of container. // This is not thread-safe. For external purpose use mSafeDeviceInfos. private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>(); @@ -167,12 +167,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { /** * Performs the action 'device select', or 'one touch play' initiated by TV. * - * @param targetAddress logical address of the device to select + * @param id id of HDMI device to select * @param callback callback object to report the result with */ @ServiceThreadOnly - void deviceSelect(int targetAddress, IHdmiControlCallback callback) { + void deviceSelect(int id, IHdmiControlCallback callback) { assertRunOnServiceThread(); + HdmiDeviceInfo targetDevice = mDeviceInfos.get(id); + if (targetDevice == null) { + invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); + return; + } + // TODO: Handle MHL device + int targetAddress = targetDevice.getLogicalAddress(); ActiveSource active = getActiveSource(); if (active.isValid() && targetAddress == active.logicalAddress) { invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS); @@ -187,18 +194,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return; } if (!mService.isControlEnabled()) { - HdmiDeviceInfo info = getDeviceInfo(targetAddress); - if (info != null) { - setActiveSource(info); - } + setActiveSource(targetDevice); invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE); return; } - HdmiDeviceInfo targetDevice = getDeviceInfo(targetAddress); - if (targetDevice == null) { - invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE); - return; - } removeAction(DeviceSelectAction.class); addAndStartAction(new DeviceSelectAction(this, targetDevice, callback)); } @@ -231,7 +230,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } setActiveSource(newActive); int logicalAddress = newActive.logicalAddress; - if (getDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) { + if (getCecDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) { if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) { setPrevPortId(getActivePortId()); } @@ -279,7 +278,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // Show OSD port change banner if (notifyInputChange) { ActiveSource activeSource = getActiveSource(); - HdmiDeviceInfo info = getDeviceInfo(activeSource.logicalAddress); + HdmiDeviceInfo info = getCecDeviceInfo(activeSource.logicalAddress); if (info == null) { info = new HdmiDeviceInfo(Constants.ADDR_INVALID, path, portId, HdmiDeviceInfo.DEVICE_RESERVED, 0, null); @@ -363,7 +362,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); int logicalAddress = message.getSource(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); - if (getDeviceInfo(logicalAddress) == null) { + if (getCecDeviceInfo(logicalAddress) == null) { handleNewDeviceAtTheTailOfActivePath(physicalAddress); } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); @@ -389,7 +388,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (portId != Constants.INVALID_PORT_ID) { // TODO: Do this only if TV is not showing multiview like PIP/PAP. - HdmiDeviceInfo inactiveSource = getDeviceInfo(message.getSource()); + HdmiDeviceInfo inactiveSource = getCecDeviceInfo(message.getSource()); if (inactiveSource == null) { return true; } @@ -577,7 +576,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleSetOsdName(HdmiCecMessage message) { int source = message.getSource(); - HdmiDeviceInfo deviceInfo = getDeviceInfo(source); + HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source); // If the device is not in device list, ignore it. if (deviceInfo == null) { Slog.e(TAG, "No source device info for <Set Osd Name>." + message); @@ -952,11 +951,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly private HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) { assertRunOnServiceThread(); - HdmiDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress()); + HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress()); if (oldDeviceInfo != null) { - removeDeviceInfo(deviceInfo.getLogicalAddress()); + removeDeviceInfo(deviceInfo.getId()); } - mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo); + mDeviceInfos.append(deviceInfo.getId(), deviceInfo); updateSafeDeviceInfoList(); return oldDeviceInfo; } @@ -967,15 +966,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. * - * @param logicalAddress logical address of device to be removed + * @param id id of device to be removed * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null} */ @ServiceThreadOnly - private HdmiDeviceInfo removeDeviceInfo(int logicalAddress) { + private HdmiDeviceInfo removeDeviceInfo(int id) { assertRunOnServiceThread(); - HdmiDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress); + HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id); if (deviceInfo != null) { - mDeviceInfos.remove(logicalAddress); + mDeviceInfos.remove(id); } updateSafeDeviceInfoList(); return deviceInfo; @@ -1035,7 +1034,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>(); for (int i = 0; i < mDeviceInfos.size(); ++i) { HdmiDeviceInfo info = mDeviceInfos.valueAt(i); - if (isLocalDeviceAddress(i)) { + if (isLocalDeviceAddress(info.getLogicalAddress())) { continue; } if (info.isSourceType() && !hideDevicesBehindLegacySwitch(info)) { @@ -1095,23 +1094,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly HdmiDeviceInfo getAvrDeviceInfo() { assertRunOnServiceThread(); - return getDeviceInfo(Constants.ADDR_AUDIO_SYSTEM); + return getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM); } /** * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}. * - * <p>Declared as package-private. accessed by {@link HdmiControlService} only. - * This is not thread-safe. For thread safety, call {@link #getSafeDeviceInfo(int)}. + * This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}. * - * @param logicalAddress logical address to be retrieved + * @param address logical address of the device to be retrieved * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}. * Returns null if no logical address matched */ @ServiceThreadOnly - HdmiDeviceInfo getDeviceInfo(int logicalAddress) { + HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); - return mDeviceInfos.get(logicalAddress); + return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress)); } boolean hasSystemAudioDevice() { @@ -1119,19 +1117,24 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } HdmiDeviceInfo getSafeAvrDeviceInfo() { - return getSafeDeviceInfo(Constants.ADDR_AUDIO_SYSTEM); + return getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM); } /** - * Thread safe version of {@link #getDeviceInfo(int)}. + * Thread safe version of {@link #getCecDeviceInfo(int)}. * * @param logicalAddress logical address to be retrieved * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}. * Returns null if no logical address matched */ - HdmiDeviceInfo getSafeDeviceInfo(int logicalAddress) { + HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) { synchronized (mLock) { - return mSafeAllDeviceInfos.get(logicalAddress); + for (HdmiDeviceInfo info : mSafeAllDeviceInfos) { + if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) { + return info; + } + } + return null; } } @@ -1160,7 +1163,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly final void removeCecDevice(int address) { assertRunOnServiceThread(); - HdmiDeviceInfo info = removeDeviceInfo(address); + HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address)); mCecMessageCache.flushMessagesFrom(address); invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE); @@ -1240,7 +1243,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly boolean isInDeviceList(int logicalAddress, int physicalAddress) { assertRunOnServiceThread(); - HdmiDeviceInfo device = getDeviceInfo(logicalAddress); + HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress); if (device == null) { return false; } @@ -1427,7 +1430,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } private boolean checkRecorder(int recorderAddress) { - HdmiDeviceInfo device = getDeviceInfo(recorderAddress); + HdmiDeviceInfo device = getCecDeviceInfo(recorderAddress); return (device != null) && (HdmiUtils.getTypeFromAddress(recorderAddress) == HdmiDeviceInfo.DEVICE_RECORDER); @@ -1528,7 +1531,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) { - HdmiDeviceInfo info = getDeviceInfo(logicalAddress); + HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); if (info == null) { Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress); return; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 0855bfa..b53cd45 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -338,6 +338,21 @@ public class HdmiCecMessageBuilder { } /** + * Build <Report Menu Status> command. + * + * @param src source address of command + * @param dest destination address of command + * @param menuStatus menu status of the device + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) { + byte[] param = new byte[] { + (byte) (menuStatus) + }; + return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param); + } + + /** * Build <System Audio Mode Request> command. * * @param src source address of command diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2818ea7..14c066e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -568,7 +568,7 @@ public final class HdmiControlService extends SystemService { if (tv == null) { return null; } - return tv.getDeviceInfo(logicalAddress); + return tv.getCecDeviceInfo(logicalAddress); } /** @@ -949,7 +949,7 @@ public final class HdmiControlService extends SystemService { } @Override - public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) { + public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override @@ -964,7 +964,7 @@ public final class HdmiControlService extends SystemService { invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); return; } - tv.deviceSelect(logicalAddress, callback); + tv.deviceSelect(deviceId, callback); } }); } @@ -996,12 +996,21 @@ public final class HdmiControlService extends SystemService { runOnServiceThread(new Runnable() { @Override public void run() { - HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType); - if (localDevice == null) { - Slog.w(TAG, "Local device not available"); - return; + if (mMhlController != null) { + HdmiMhlLocalDevice device = mMhlController.getLocalDevice(mActivePortId); + if (device != null) { + device.sendKeyEvent(keyCode, isPressed); + return; + } + } + if (mCecController != null) { + HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType); + if (localDevice == null) { + Slog.w(TAG, "Local device not available"); + return; + } + localDevice.sendKeyEvent(keyCode, isPressed); } - localDevice.sendKeyEvent(keyCode, isPressed); } }); } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index 51e68b6..722be71 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -207,7 +207,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { } private void mayChangeRoutingPath(int address) { - HdmiDeviceInfo info = tv().getDeviceInfo(address); + HdmiDeviceInfo info = tv().getCecDeviceInfo(address); if (info != null) { tv().handleRemoveActiveRoutingPath(info.getPhysicalAddress()); } diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index 94cf668..9dcc529 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -23,6 +23,7 @@ import android.content.pm.PackageManager; import android.os.Handler; import android.os.IHardwareService; import android.os.Message; +import android.os.Trace; import android.util.Slog; import java.io.FileInputStream; @@ -105,7 +106,12 @@ public class LightsService extends SystemService { mMode = mode; mOnMS = onMS; mOffMS = offMS; - setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", " + color + ")"); + try { + setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } } } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index a06daf6..189131c 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -51,8 +51,9 @@ public class ConditionProviders extends ManagedServices { = new ArrayMap<IBinder, IConditionListener>(); private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>(); private final CountdownConditionProvider mCountdown = new CountdownConditionProvider(); + private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider(); - private Uri mExitConditionId; + private Condition mExitCondition; private ComponentName mExitConditionComponent; public ConditionProviders(Context context, Handler handler, @@ -97,6 +98,7 @@ public class ConditionProviders extends ManagedServices { } } mCountdown.dump(pw, filter); + mDowntime.dump(pw, filter); } @Override @@ -110,6 +112,10 @@ public class ConditionProviders extends ManagedServices { mCountdown.attachBase(mContext); registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT, UserHandle.USER_OWNER); + mDowntime.attachBase(mContext); + registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT, + UserHandle.USER_OWNER); + mDowntime.setCallback(new DowntimeCallback()); } @Override @@ -125,7 +131,7 @@ public class ConditionProviders extends ManagedServices { if (info.component.equals(mExitConditionComponent)) { // ensure record exists, we'll wire it up and subscribe below final ConditionRecord manualRecord = - getRecordLocked(mExitConditionId, mExitConditionComponent); + getRecordLocked(mExitCondition.id, mExitConditionComponent); manualRecord.isManual = true; } final int N = mRecords.size(); @@ -149,11 +155,11 @@ public class ConditionProviders extends ManagedServices { if (!r.component.equals(removed.component)) continue; if (r.isManual) { // removing the current manual condition, exit zen - mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); + mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved"); } if (r.isAutomatic) { // removing an automatic condition, exit zen - mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); + mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "automaticServiceRemoved"); } mRecords.remove(i); } @@ -249,7 +255,8 @@ public class ConditionProviders extends ManagedServices { } else if (DEBUG) { Slog.d(TAG, "Exit zen: manual condition false: " + c); } - mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF); + mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF, + "manualConditionExit"); unsubscribeLocked(r); r.isManual = false; } @@ -263,33 +270,46 @@ public class ConditionProviders extends ManagedServices { } else if (DEBUG) { Slog.d(TAG, "Exit zen: automatic condition false: " + c); } - mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF); + mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF, + "automaticConditionExit"); } else if (c.state == Condition.STATE_TRUE) { Slog.d(TAG, "Enter zen: automatic condition true: " + c); - mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); + mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, + "automaticConditionEnter"); } } } } } - public void setZenModeCondition(Uri conditionId, String reason) { - if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId); + public void setZenModeCondition(Condition condition, String reason) { + if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition); synchronized(mMutex) { ComponentName conditionComponent = null; - if (ZenModeConfig.isValidCountdownConditionId(conditionId)) { - // constructed by the client, make sure the record exists... - final ConditionRecord r = getRecordLocked(conditionId, - CountdownConditionProvider.COMPONENT); - if (r.info == null) { - // ... and is associated with the in-process service - r.info = checkServiceTokenLocked(mCountdown.asInterface()); + if (condition != null) { + if (ZenModeConfig.isValidCountdownConditionId(condition.id)) { + // constructed by the client, make sure the record exists... + final ConditionRecord r = getRecordLocked(condition.id, + CountdownConditionProvider.COMPONENT); + if (r.info == null) { + // ... and is associated with the in-process service + r.info = checkServiceTokenLocked(mCountdown.asInterface()); + } + } + if (ZenModeConfig.isValidDowntimeConditionId(condition.id)) { + // constructed by the client, make sure the record exists... + final ConditionRecord r = getRecordLocked(condition.id, + DowntimeConditionProvider.COMPONENT); + if (r.info == null) { + // ... and is associated with the in-process service + r.info = checkServiceTokenLocked(mDowntime.asInterface()); + } } } final int N = mRecords.size(); for (int i = 0; i < N; i++) { final ConditionRecord r = mRecords.get(i); - final boolean idEqual = r.id.equals(conditionId); + final boolean idEqual = condition != null && r.id.equals(condition.id); if (r.isManual && !idEqual) { // was previous manual condition, unsubscribe unsubscribeLocked(r); @@ -303,10 +323,10 @@ public class ConditionProviders extends ManagedServices { conditionComponent = r.component; } } - if (!Objects.equals(mExitConditionId, conditionId)) { - mExitConditionId = conditionId; + if (!Objects.equals(mExitCondition, condition)) { + mExitCondition = condition; mExitConditionComponent = conditionComponent; - ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, reason); + ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, reason); saveZenConfigLocked(); } } @@ -318,6 +338,7 @@ public class ConditionProviders extends ManagedServices { RemoteException re = null; if (provider != null) { try { + Slog.d(TAG, "Subscribing to " + r.id + " with " + provider); provider.onSubscribe(r.id); } catch (RemoteException e) { Slog.w(TAG, "Error subscribing to " + r, e); @@ -436,12 +457,13 @@ public class ConditionProviders extends ManagedServices { return; } synchronized (mMutex) { - final boolean changingExit = !Objects.equals(mExitConditionId, config.exitConditionId); - mExitConditionId = config.exitConditionId; + final boolean changingExit = !Objects.equals(mExitCondition, config.exitCondition); + mExitCondition = config.exitCondition; mExitConditionComponent = config.exitConditionComponent; if (changingExit) { - ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, "config"); + ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config"); } + mDowntime.setConfig(config); if (config.conditionComponents == null || config.conditionIds == null || config.conditionComponents.length != config.conditionIds.length) { if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions"); @@ -488,7 +510,7 @@ public class ConditionProviders extends ManagedServices { config.conditionIds[i] = r.id; } } - config.exitConditionId = mExitConditionId; + config.exitCondition = mExitCondition; config.exitConditionComponent = mExitConditionComponent; if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config); mZenModeHelper.setConfig(config); @@ -510,6 +532,26 @@ public class ConditionProviders extends ManagedServices { } } + private class DowntimeCallback implements DowntimeConditionProvider.Callback { + @Override + public void onDowntimeChanged(boolean inDowntime) { + final int mode = mZenModeHelper.getZenMode(); + final ZenModeConfig config = mZenModeHelper.getConfig(); + // enter downtime + if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) { + final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(), + Condition.STATE_TRUE); + mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter"); + setZenModeCondition(condition, "downtime"); + } + // exit downtime + if (!inDowntime && mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + && mDowntime.isDowntimeCondition(mExitCondition)) { + mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit"); + } + } + } + private static class ConditionRecord { public final Uri id; public final ComponentName component; diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java index aaf7cfc..37aacaa 100644 --- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java +++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java @@ -29,6 +29,7 @@ import android.service.notification.ConditionProviderService; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; +import android.util.Log; import android.util.Slog; import com.android.server.notification.NotificationManagerService.DumpFilter; @@ -38,8 +39,8 @@ import java.util.Date; /** Built-in zen condition provider for simple time-based conditions */ public class CountdownConditionProvider extends ConditionProviderService { - private static final String TAG = "CountdownConditionProvider"; - private static final boolean DEBUG = false; + private static final String TAG = "CountdownConditions"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final ComponentName COMPONENT = new ComponentName("android", CountdownConditionProvider.class.getName()); diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java new file mode 100644 index 0000000..317ebef --- /dev/null +++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java @@ -0,0 +1,289 @@ +/** + * Copyright (c) 2014, 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.notification; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.service.notification.Condition; +import android.service.notification.ConditionProviderService; +import android.service.notification.IConditionProvider; +import android.service.notification.ZenModeConfig; +import android.service.notification.ZenModeConfig.DowntimeInfo; +import android.text.format.DateFormat; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.R; +import com.android.server.notification.NotificationManagerService.DumpFilter; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.Objects; + +/** Built-in zen condition provider for managing downtime */ +public class DowntimeConditionProvider extends ConditionProviderService { + private static final String TAG = "DowntimeConditions"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final ComponentName COMPONENT = + new ComponentName("android", DowntimeConditionProvider.class.getName()); + + private static final String ENTER_ACTION = TAG + ".enter"; + private static final int ENTER_CODE = 100; + private static final String EXIT_ACTION = TAG + ".exit"; + private static final int EXIT_CODE = 101; + private static final String EXTRA_TIME = "time"; + + private final Calendar mCalendar = Calendar.getInstance(); + private final Context mContext = this; + private final ArraySet<Integer> mDays = new ArraySet<Integer>(); + + private boolean mConnected; + private boolean mInDowntime; + private ZenModeConfig mConfig; + private Callback mCallback; + + public DowntimeConditionProvider() { + if (DEBUG) Slog.d(TAG, "new DowntimeConditionProvider()"); + } + + public void dump(PrintWriter pw, DumpFilter filter) { + pw.println(" DowntimeConditionProvider:"); + pw.print(" mConnected="); pw.println(mConnected); + pw.print(" mInDowntime="); pw.println(mInDowntime); + } + + public void attachBase(Context base) { + attachBaseContext(base); + } + + public IConditionProvider asInterface() { + return (IConditionProvider) onBind(null); + } + + public void setCallback(Callback callback) { + mCallback = callback; + } + + @Override + public void onConnected() { + if (DEBUG) Slog.d(TAG, "onConnected"); + mConnected = true; + final IntentFilter filter = new IntentFilter(); + filter.addAction(ENTER_ACTION); + filter.addAction(EXIT_ACTION); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + mContext.registerReceiver(mReceiver, filter); + init(); + } + + @Override + public void onDestroy() { + if (DEBUG) Slog.d(TAG, "onDestroy"); + mConnected = false; + } + + @Override + public void onRequestConditions(int relevance) { + if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance); + if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) { + if (mInDowntime && mConfig != null) { + notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE)); + } + } + } + + @Override + public void onSubscribe(Uri conditionId) { + if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId); + final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId); + if (downtime != null && mConfig != null) { + final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime + ? Condition.STATE_TRUE : Condition.STATE_FALSE; + if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state)); + notifyCondition(createCondition(downtime, state)); + } + } + + @Override + public void onUnsubscribe(Uri conditionId) { + if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId); + } + + public void setConfig(ZenModeConfig config) { + if (Objects.equals(mConfig, config)) return; + if (DEBUG) Slog.d(TAG, "setConfig"); + mConfig = config; + if (mConnected) { + init(); + } + } + + public boolean isInDowntime() { + return mInDowntime; + } + + public Condition createCondition(DowntimeInfo downtime, int state) { + if (downtime == null) return null; + final Uri id = ZenModeConfig.toDowntimeConditionId(downtime); + final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma"; + final Locale locale = Locale.getDefault(); + final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton); + final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute); + final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time)); + final String summary = mContext.getString(R.string.downtime_condition_summary, formatted); + return new Condition(id, summary, "", "", 0, state, Condition.FLAG_RELEVANT_NOW); + } + + public boolean isDowntimeCondition(Condition condition) { + return condition != null && ZenModeConfig.isValidDowntimeConditionId(condition.id); + } + + private void init() { + updateDays(); + reevaluateDowntime(); + updateAlarms(); + } + + private void updateDays() { + mDays.clear(); + if (mConfig != null) { + final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode); + for (int i = 0; days != null && i < days.length; i++) { + mDays.add(days[i]); + } + } + } + + private boolean isInDowntime(long time) { + if (mConfig == null || mDays.size() == 0) return false; + final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute); + long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute); + if (start == end) return false; + if (end < start) { + end = addDays(end, 1); + } + return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end); + } + + private boolean isInDowntime(int daysOffset, long time, long start, long end) { + final int day = ((getDayOfWeek(time) + daysOffset - 1) % Calendar.SATURDAY) + 1; + start = addDays(start, daysOffset); + end = addDays(end, daysOffset); + return mDays.contains(day) && time >= start && time < end; + } + + private void reevaluateDowntime() { + final boolean inDowntime = isInDowntime(System.currentTimeMillis()); + if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime); + if (inDowntime == mInDowntime) return; + Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime"); + mInDowntime = inDowntime; + ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays); + fireDowntimeChanged(); + } + + private void fireDowntimeChanged() { + if (mCallback != null) { + mCallback.onDowntimeChanged(mInDowntime); + } + } + + private void updateAlarms() { + if (mConfig == null) return; + updateAlarm(ENTER_ACTION, ENTER_CODE, mConfig.sleepStartHour, mConfig.sleepStartMinute); + updateAlarm(EXIT_ACTION, EXIT_CODE, mConfig.sleepEndHour, mConfig.sleepEndMinute); + } + + private int getDayOfWeek(long time) { + mCalendar.setTimeInMillis(time); + return mCalendar.get(Calendar.DAY_OF_WEEK); + } + + private long getTime(long millis, int hour, int min) { + mCalendar.setTimeInMillis(millis); + mCalendar.set(Calendar.HOUR_OF_DAY, hour); + mCalendar.set(Calendar.MINUTE, min); + mCalendar.set(Calendar.SECOND, 0); + mCalendar.set(Calendar.MILLISECOND, 0); + return mCalendar.getTimeInMillis(); + } + + private long addDays(long time, int days) { + mCalendar.setTimeInMillis(time); + mCalendar.add(Calendar.DATE, days); + return mCalendar.getTimeInMillis(); + } + + private void updateAlarm(String action, int requestCode, int hr, int min) { + final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + final long now = System.currentTimeMillis(); + mCalendar.setTimeInMillis(now); + mCalendar.set(Calendar.HOUR_OF_DAY, hr); + mCalendar.set(Calendar.MINUTE, min); + mCalendar.set(Calendar.SECOND, 0); + mCalendar.set(Calendar.MILLISECOND, 0); + long time = mCalendar.getTimeInMillis(); + if (time <= now) { + time = addDays(time, 1); + } + final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode, + new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT); + alarms.cancel(pendingIntent); + if (mConfig.sleepMode != null) { + if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s", + action, ts(time), time - now, ts(now))); + alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent); + } + } + + private static String ts(long time) { + return new Date(time) + " (" + time + ")"; + } + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final long now = System.currentTimeMillis(); + if (ENTER_ACTION.equals(action) || EXIT_ACTION.equals(action)) { + final long schTime = intent.getLongExtra(EXTRA_TIME, 0); + if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s", + action, ts(schTime), ts(now), now - schTime)); + } else { + if (DEBUG) Slog.d(TAG, action + " fired at " + now); + } + reevaluateDowntime(); + updateAlarms(); + } + }; + + public interface Callback { + void onDowntimeChanged(boolean inDowntime); + } +} diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 36be21f..f647037 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -41,6 +41,7 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -64,7 +65,7 @@ import java.util.Set; */ abstract public class ManagedServices { protected final String TAG = getClass().getSimpleName(); - protected static final boolean DEBUG = true; + protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String ENABLED_SERVICES_SEPARATOR = ":"; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d6afe68..f2ac963 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1351,11 +1351,11 @@ public class NotificationManagerService extends SystemService { } @Override - public void setZenModeCondition(Uri conditionId) { + public void setZenModeCondition(Condition condition) { enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); final long identity = Binder.clearCallingIdentity(); try { - mConditionProviders.setZenModeCondition(conditionId, "binderCall"); + mConditionProviders.setZenModeCondition(condition, "binderCall"); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java index b22ed2d..525f5f8 100644 --- a/services/core/java/com/android/server/notification/ZenLog.java +++ b/services/core/java/com/android/server/notification/ZenLog.java @@ -22,8 +22,10 @@ import android.net.Uri; import android.os.Build; import android.os.RemoteException; import android.provider.Settings.Global; +import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; +import android.util.ArraySet; import android.util.Slog; import java.io.PrintWriter; @@ -52,13 +54,14 @@ public class ZenLog { private static final int TYPE_ALLOW_DISABLE = 2; private static final int TYPE_SET_RINGER_MODE = 3; private static final int TYPE_DOWNTIME = 4; - private static final int TYPE_ZEN_MODE = 5; - private static final int TYPE_EXIT_CONDITION = 6; - private static final int TYPE_SUBSCRIBE = 7; - private static final int TYPE_UNSUBSCRIBE = 8; - private static final int TYPE_CONFIG = 9; - private static final int TYPE_FOLLOW_RINGER_MODE = 10; - private static final int TYPE_NOT_INTERCEPTED = 11; + private static final int TYPE_SET_ZEN_MODE = 5; + private static final int TYPE_UPDATE_ZEN_MODE = 6; + private static final int TYPE_EXIT_CONDITION = 7; + private static final int TYPE_SUBSCRIBE = 8; + private static final int TYPE_UNSUBSCRIBE = 9; + private static final int TYPE_CONFIG = 10; + private static final int TYPE_FOLLOW_RINGER_MODE = 11; + private static final int TYPE_NOT_INTERCEPTED = 12; private static int sNext; private static int sSize; @@ -82,17 +85,20 @@ public class ZenLog { append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode)); } - public static void traceDowntime(boolean enter, int day, int[] days) { - append(TYPE_DOWNTIME, enter + ",day=" + day + ",days=" + (days != null ? Arrays.asList(days) - : null)); + public static void traceDowntime(boolean inDowntime, int day, ArraySet<Integer> days) { + append(TYPE_DOWNTIME, inDowntime + ",day=" + day + ",days=" + days); + } + + public static void traceSetZenMode(int mode, String reason) { + append(TYPE_SET_ZEN_MODE, zenModeToString(mode) + "," + reason); } public static void traceUpdateZenMode(int fromMode, int toMode) { - append(TYPE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode)); + append(TYPE_UPDATE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode)); } - public static void traceExitCondition(Uri id, ComponentName component, String reason) { - append(TYPE_EXIT_CONDITION, id + "," + componentToString(component) + "," + reason); + public static void traceExitCondition(Condition c, ComponentName component, String reason) { + append(TYPE_EXIT_CONDITION, c + "," + componentToString(component) + "," + reason); } public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) { @@ -122,7 +128,8 @@ public class ZenLog { case TYPE_ALLOW_DISABLE: return "allow_disable"; case TYPE_SET_RINGER_MODE: return "set_ringer_mode"; case TYPE_DOWNTIME: return "downtime"; - case TYPE_ZEN_MODE: return "zen_mode"; + case TYPE_SET_ZEN_MODE: return "set_zen_mode"; + case TYPE_UPDATE_ZEN_MODE: return "update_zen_mode"; case TYPE_EXIT_CONDITION: return "exit_condition"; case TYPE_SUBSCRIBE: return "subscribe"; case TYPE_UNSUBSCRIBE: return "unsubscribe"; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 9282283..758f334 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -20,10 +20,8 @@ import static android.media.AudioAttributes.USAGE_ALARM; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static android.media.AudioAttributes.USAGE_UNKNOWN; -import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.Notification; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -44,6 +42,7 @@ import android.provider.Settings.Secure; import android.service.notification.NotificationListenerService; import android.service.notification.ZenModeConfig; import android.telecomm.TelecommManager; +import android.util.Log; import android.util.Slog; import com.android.internal.R; @@ -57,8 +56,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; import java.util.Objects; /** @@ -66,12 +63,7 @@ import java.util.Objects; */ public class ZenModeHelper { private static final String TAG = "ZenModeHelper"; - - private static final String ACTION_ENTER_ZEN = "enter_zen"; - private static final int REQUEST_CODE_ENTER = 100; - private static final String ACTION_EXIT_ZEN = "exit_zen"; - private static final int REQUEST_CODE_EXIT = 101; - private static final String EXTRA_TIME = "time"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final Context mContext; private final Handler mHandler; @@ -96,10 +88,8 @@ public class ZenModeHelper { mSettingsObserver.observe(); final IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_ENTER_ZEN); - filter.addAction(ACTION_EXIT_ZEN); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - mContext.registerReceiver(new ZenBroadcastReceiver(), filter); + mContext.registerReceiver(mReceiver, filter); } public static ZenModeConfig readDefaultConfig(Resources resources) { @@ -156,7 +146,7 @@ public class ZenModeHelper { public void requestFromListener(int hints) { final int newZen = zenFromListenerHint(hints, -1); if (newZen != -1) { - setZenMode(newZen); + setZenMode(newZen, "listener"); } } @@ -179,24 +169,19 @@ public class ZenModeHelper { return false; } } - // audience has veto power over all following rules - if (!audienceMatches(record)) { - ZenLog.traceIntercepted(record, "!audienceMatches"); - return true; - } if (isCall(record)) { if (!mConfig.allowCalls) { ZenLog.traceIntercepted(record, "!allowCalls"); return true; } - return false; + return shouldInterceptAudience(record); } if (isMessage(record)) { if (!mConfig.allowMessages) { ZenLog.traceIntercepted(record, "!allowMessages"); return true; } - return false; + return shouldInterceptAudience(record); } ZenLog.traceIntercepted(record, "!allowed"); return true; @@ -204,11 +189,20 @@ public class ZenModeHelper { return false; } + private boolean shouldInterceptAudience(NotificationRecord record) { + if (!audienceMatches(record)) { + ZenLog.traceIntercepted(record, "!audienceMatches"); + return true; + } + return false; + } + public int getZenMode() { return mZenMode; } - public void setZenMode(int zenModeValue) { + public void setZenMode(int zenModeValue, String reason) { + ZenLog.traceSetZenMode(zenModeValue, reason); Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue); } @@ -216,9 +210,6 @@ public class ZenModeHelper { final int mode = Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF); if (mode != mZenMode) { - Slog.d(TAG, String.format("updateZenMode: %s -> %s", - Global.zenModeToString(mZenMode), - Global.zenModeToString(mode))); ZenLog.traceUpdateZenMode(mZenMode, mode); } mZenMode = mode; @@ -255,12 +246,12 @@ public class ZenModeHelper { if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) { if (ringerMode != AudioManager.RINGER_MODE_SILENT) { mPreviousRingerMode = ringerMode; - Slog.d(TAG, "Silencing ringer"); + if (DEBUG) Slog.d(TAG, "Silencing ringer"); forcedRingerMode = AudioManager.RINGER_MODE_SILENT; } } else { if (ringerMode == AudioManager.RINGER_MODE_SILENT) { - Slog.d(TAG, "Unsilencing ringer"); + if (DEBUG) Slog.d(TAG, "Unsilencing ringer"); forcedRingerMode = mPreviousRingerMode != -1 ? mPreviousRingerMode : AudioManager.RINGER_MODE_NORMAL; mPreviousRingerMode = -1; @@ -318,7 +309,6 @@ public class ZenModeHelper { dispatchOnConfigChanged(); final String val = Integer.toString(mConfig.hashCode()); Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); - updateAlarms(); updateZenMode(); return true; } @@ -339,7 +329,7 @@ public class ZenModeHelper { } if (newZen != -1) { ZenLog.traceFollowRingerMode(ringerMode, mZenMode, newZen); - setZenMode(newZen); + setZenMode(newZen, "ringerMode"); } } } @@ -377,7 +367,7 @@ public class ZenModeHelper { final TelecommManager telecomm = (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE); mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null; - Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp); + if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp); } return pkg != null && mDefaultPhoneApp != null && pkg.equals(mDefaultPhoneApp.getPackageName()); @@ -409,40 +399,6 @@ public class ZenModeHelper { } } - private void updateAlarms() { - updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER, - mConfig.sleepStartHour, mConfig.sleepStartMinute); - updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT, - mConfig.sleepEndHour, mConfig.sleepEndMinute); - } - - private void updateAlarm(String action, int requestCode, int hr, int min) { - final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - final long now = System.currentTimeMillis(); - final Calendar c = Calendar.getInstance(); - c.setTimeInMillis(now); - c.set(Calendar.HOUR_OF_DAY, hr); - c.set(Calendar.MINUTE, min); - c.set(Calendar.SECOND, 0); - c.set(Calendar.MILLISECOND, 0); - if (c.getTimeInMillis() <= now) { - c.add(Calendar.DATE, 1); - } - final long time = c.getTimeInMillis(); - final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode, - new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT); - alarms.cancel(pendingIntent); - if (mConfig.sleepMode != null) { - Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s", - action, ts(time), time - now, ts(now))); - alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent); - } - } - - private static String ts(long time) { - return new Date(time) + " (" + time + ")"; - } - private final Runnable mRingerModeChanged = new Runnable() { @Override public void run() { @@ -475,47 +431,12 @@ public class ZenModeHelper { } } - private class ZenBroadcastReceiver extends BroadcastReceiver { - private final Calendar mCalendar = Calendar.getInstance(); - + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (ACTION_ENTER_ZEN.equals(intent.getAction())) { - setZenMode(intent, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); - } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) { - setZenMode(intent, Global.ZEN_MODE_OFF); - } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) { - mHandler.post(mRingerModeChanged); - } - } - - private void setZenMode(Intent intent, int zenModeValue) { - final long schTime = intent.getLongExtra(EXTRA_TIME, 0); - final long now = System.currentTimeMillis(); - Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s", - intent.getAction(), ts(schTime), ts(now), now - schTime)); - - final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode); - boolean enter = false; - final int day = getDayOfWeek(schTime); - if (days != null) { - for (int i = 0; i < days.length; i++) { - if (days[i] == day) { - enter = true; - ZenModeHelper.this.setZenMode(zenModeValue); - break; - } - } - } - ZenLog.traceDowntime(enter, day, days); - updateAlarms(); + mHandler.post(mRingerModeChanged); } - - private int getDayOfWeek(long time) { - mCalendar.setTimeInMillis(time); - return mCalendar.get(Calendar.DAY_OF_WEEK); - } - } + }; public static class Callback { void onConfigChanged() {} diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index b261ef5..d1e03ec 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -16,33 +16,23 @@ package com.android.server.pm; -import com.android.server.SystemService; - import android.content.Context; import android.content.pm.PackageStats; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; +import android.os.Build; import android.util.Slog; +import dalvik.system.VMRuntime; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; +import com.android.internal.os.InstallerConnection; +import com.android.server.SystemService; public final class Installer extends SystemService { private static final String TAG = "Installer"; - private static final boolean LOCAL_DEBUG = false; - - InputStream mIn; - OutputStream mOut; - LocalSocket mSocket; - - byte buf[] = new byte[1024]; - int buflen = 0; + private final InstallerConnection mInstaller; public Installer(Context context) { super(context); + mInstaller = new InstallerConnection(); } @Override @@ -51,154 +41,6 @@ public final class Installer extends SystemService { ping(); } - private boolean connect() { - if (mSocket != null) { - return true; - } - Slog.i(TAG, "connecting..."); - try { - mSocket = new LocalSocket(); - - LocalSocketAddress address = new LocalSocketAddress("installd", - LocalSocketAddress.Namespace.RESERVED); - - mSocket.connect(address); - - mIn = mSocket.getInputStream(); - mOut = mSocket.getOutputStream(); - } catch (IOException ex) { - disconnect(); - return false; - } - return true; - } - - private void disconnect() { - Slog.i(TAG, "disconnecting..."); - try { - if (mSocket != null) - mSocket.close(); - } catch (IOException ex) { - } - try { - if (mIn != null) - mIn.close(); - } catch (IOException ex) { - } - try { - if (mOut != null) - mOut.close(); - } catch (IOException ex) { - } - mSocket = null; - mIn = null; - mOut = null; - } - - private boolean readBytes(byte buffer[], int len) { - int off = 0, count; - if (len < 0) - return false; - while (off != len) { - try { - count = mIn.read(buffer, off, len - off); - if (count <= 0) { - Slog.e(TAG, "read error " + count); - break; - } - off += count; - } catch (IOException ex) { - Slog.e(TAG, "read exception"); - break; - } - } - if (LOCAL_DEBUG) { - Slog.i(TAG, "read " + len + " bytes"); - } - if (off == len) - return true; - disconnect(); - return false; - } - - private boolean readReply() { - int len; - buflen = 0; - if (!readBytes(buf, 2)) - return false; - len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); - if ((len < 1) || (len > 1024)) { - Slog.e(TAG, "invalid reply length (" + len + ")"); - disconnect(); - return false; - } - if (!readBytes(buf, len)) - return false; - buflen = len; - return true; - } - - private boolean writeCommand(String _cmd) { - byte[] cmd = _cmd.getBytes(); - int len = cmd.length; - if ((len < 1) || (len > 1024)) - return false; - buf[0] = (byte) (len & 0xff); - buf[1] = (byte) ((len >> 8) & 0xff); - try { - mOut.write(buf, 0, 2); - mOut.write(cmd, 0, len); - } catch (IOException ex) { - Slog.e(TAG, "write error"); - disconnect(); - return false; - } - return true; - } - - private synchronized String transaction(String cmd) { - if (!connect()) { - Slog.e(TAG, "connection failed"); - return "-1"; - } - - if (!writeCommand(cmd)) { - /* - * If installd died and restarted in the background (unlikely but - * possible) we'll fail on the next write (this one). Try to - * reconnect and write the command one more time before giving up. - */ - Slog.e(TAG, "write command failed? reconnect!"); - if (!connect() || !writeCommand(cmd)) { - return "-1"; - } - } - if (LOCAL_DEBUG) { - Slog.i(TAG, "send: '" + cmd + "'"); - } - if (readReply()) { - String s = new String(buf, 0, buflen); - if (LOCAL_DEBUG) { - Slog.i(TAG, "recv: '" + s + "'"); - } - return s; - } else { - if (LOCAL_DEBUG) { - Slog.i(TAG, "fail"); - } - return "-1"; - } - } - - private int execute(String cmd) { - String res = transaction(cmd); - try { - return Integer.parseInt(res); - } catch (NumberFormatException ex) { - return -1; - } - } - public int install(String name, int uid, int gid, String seinfo) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); @@ -209,11 +51,16 @@ public final class Installer extends SystemService { builder.append(gid); builder.append(' '); builder.append(seinfo != null ? seinfo : "!"); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName, String instructionSet) { + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + StringBuilder builder = new StringBuilder("patchoat"); builder.append(' '); builder.append(apkPath); @@ -224,37 +71,34 @@ public final class Installer extends SystemService { builder.append(pkgName); builder.append(' '); builder.append(instructionSet); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) { - StringBuilder builder = new StringBuilder("patchoat"); - builder.append(' '); - builder.append(apkPath); - builder.append(' '); - builder.append(uid); - builder.append(isPublic ? " 1" : " 0"); - builder.append(" *"); // No pkgName arg present - builder.append(' '); - builder.append(instructionSet); - return execute(builder.toString()); + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + + return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet); } public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { - StringBuilder builder = new StringBuilder("dexopt"); - builder.append(' '); - builder.append(apkPath); - builder.append(' '); - builder.append(uid); - builder.append(isPublic ? " 1" : " 0"); - builder.append(" *"); // No pkgName arg present - builder.append(' '); - builder.append(instructionSet); - return execute(builder.toString()); + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + + return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet); } public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, String instructionSet) { + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); @@ -265,7 +109,7 @@ public final class Installer extends SystemService { builder.append(pkgName); builder.append(' '); builder.append(instructionSet); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int idmap(String targetApkPath, String overlayApkPath, int uid) { @@ -276,10 +120,15 @@ public final class Installer extends SystemService { builder.append(overlayApkPath); builder.append(' '); builder.append(uid); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int movedex(String srcPath, String dstPath, String instructionSet) { + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + StringBuilder builder = new StringBuilder("movedex"); builder.append(' '); builder.append(srcPath); @@ -287,16 +136,21 @@ public final class Installer extends SystemService { builder.append(dstPath); builder.append(' '); builder.append(instructionSet); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int rmdex(String codePath, String instructionSet) { + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + StringBuilder builder = new StringBuilder("rmdex"); builder.append(' '); builder.append(codePath); builder.append(' '); builder.append(instructionSet); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int remove(String name, int userId) { @@ -305,7 +159,7 @@ public final class Installer extends SystemService { builder.append(name); builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int rename(String oldname, String newname) { @@ -314,7 +168,7 @@ public final class Installer extends SystemService { builder.append(oldname); builder.append(' '); builder.append(newname); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int fixUid(String name, int uid, int gid) { @@ -325,7 +179,7 @@ public final class Installer extends SystemService { builder.append(uid); builder.append(' '); builder.append(gid); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int deleteCacheFiles(String name, int userId) { @@ -334,7 +188,7 @@ public final class Installer extends SystemService { builder.append(name); builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int deleteCodeCacheFiles(String name, int userId) { @@ -343,7 +197,7 @@ public final class Installer extends SystemService { builder.append(name); builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int createUserData(String name, int uid, int userId, String seinfo) { @@ -356,21 +210,21 @@ public final class Installer extends SystemService { builder.append(userId); builder.append(' '); builder.append(seinfo != null ? seinfo : "!"); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int createUserConfig(int userId) { StringBuilder builder = new StringBuilder("mkuserconfig"); builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int removeUserDataDirs(int userId) { StringBuilder builder = new StringBuilder("rmuser"); builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int clearUserData(String name, int userId) { @@ -379,11 +233,11 @@ public final class Installer extends SystemService { builder.append(name); builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public boolean ping() { - if (execute("ping") < 0) { + if (mInstaller.execute("ping") < 0) { return false; } else { return true; @@ -391,18 +245,25 @@ public final class Installer extends SystemService { } public int pruneDexCache(String cacheSubDir) { - return execute("prunedexcache " + cacheSubDir); + return mInstaller.execute("prunedexcache " + cacheSubDir); } public int freeCache(long freeStorageSize) { StringBuilder builder = new StringBuilder("freecache"); builder.append(' '); builder.append(String.valueOf(freeStorageSize)); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) { + for (String instructionSet : instructionSets) { + if (!isValidInstructionSet(instructionSet)) { + Slog.e(TAG, "Invalid instruction set: " + instructionSet); + return -1; + } + } + StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); @@ -423,7 +284,7 @@ public final class Installer extends SystemService { // just the primary. builder.append(instructionSets[0]); - String s = transaction(builder.toString()); + String s = mInstaller.transact(builder.toString()); String res[] = s.split(" "); if ((res == null) || (res.length != 5)) { @@ -441,7 +302,7 @@ public final class Installer extends SystemService { } public int moveFiles() { - return execute("movefiles"); + return mInstaller.execute("movefiles"); } /** @@ -467,7 +328,7 @@ public final class Installer extends SystemService { builder.append(' '); builder.append(userId); - return execute(builder.toString()); + return mInstaller.execute(builder.toString()); } public boolean restoreconData(String pkgName, String seinfo, int uid) { @@ -478,6 +339,23 @@ public final class Installer extends SystemService { builder.append(seinfo != null ? seinfo : "!"); builder.append(' '); builder.append(uid); - return (execute(builder.toString()) == 0); + return (mInstaller.execute(builder.toString()) == 0); + } + + /** + * Returns true iff. {@code instructionSet} is a valid instruction set. + */ + private static boolean isValidInstructionSet(String instructionSet) { + if (instructionSet == null) { + return false; + } + + for (String abi : Build.SUPPORTED_ABIS) { + if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) { + return true; + } + } + + return false; } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index c7e3fb7..dca8ad4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -36,19 +36,23 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.PackageDeleteObserver; +import android.app.PackageInstallObserver; import android.content.Context; import android.content.Intent; -import android.content.pm.IPackageDeleteObserver2; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageInstallerSession; -import android.content.pm.InstallSessionInfo; -import android.content.pm.InstallSessionParams; import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -63,6 +67,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.system.ErrnoException; import android.system.Os; +import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.AtomicFile; @@ -279,8 +284,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR)); final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); - final InstallSessionParams params = new InstallSessionParams( - InstallSessionParams.MODE_INVALID); + final SessionParams params = new SessionParams( + SessionParams.MODE_INVALID); params.mode = readIntAttribute(in, ATTR_MODE); params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); @@ -292,9 +297,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub { params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); - return new PackageInstallerSession(mInternalCallback, mPm, mInstallThread.getLooper(), - sessionId, userId, installerPackageName, params, createdMillis, sessionStageDir, - sealed); + return new PackageInstallerSession(mInternalCallback, mContext, mPm, + mInstallThread.getLooper(), sessionId, userId, installerPackageName, params, + createdMillis, sessionStageDir, sealed); } private void writeSessionsLocked() { @@ -326,7 +331,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session) throws IOException { - final InstallSessionParams params = session.params; + final SessionParams params = session.params; final Snapshot snapshot = session.snapshot(); out.startTag(null, TAG_SESSION); @@ -366,7 +371,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @Override - public int createSession(InstallSessionParams params, String installerPackageName, int userId) { + public int createSession(SessionParams params, String installerPackageName, int userId) { final int callingUid = Binder.getCallingUid(); mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession"); @@ -389,8 +394,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } switch (params.mode) { - case InstallSessionParams.MODE_FULL_INSTALL: - case InstallSessionParams.MODE_INHERIT_EXISTING: + case SessionParams.MODE_FULL_INSTALL: + case SessionParams.MODE_INHERIT_EXISTING: break; default: throw new IllegalArgumentException("Params must have valid mode set"); @@ -437,7 +442,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { final long createdMillis = System.currentTimeMillis(); final File sessionStageDir = prepareSessionStageDir(sessionId); - session = new PackageInstallerSession(mInternalCallback, mPm, + session = new PackageInstallerSession(mInternalCallback, mContext, mPm, mInstallThread.getLooper(), sessionId, userId, installerPackageName, params, createdMillis, sessionStageDir, false); mSessions.put(sessionId, session); @@ -501,7 +506,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @Override - public InstallSessionInfo getSessionInfo(int sessionId) { + public SessionInfo getSessionInfo(int sessionId) { synchronized (mSessions) { final PackageInstallerSession session = mSessions.get(sessionId); if (!isCallingUidOwner(session)) { @@ -512,11 +517,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @Override - public List<InstallSessionInfo> getAllSessions(int userId) { + public List<SessionInfo> getAllSessions(int userId) { mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions"); enforceCallerCanReadSessions(); - final List<InstallSessionInfo> result = new ArrayList<>(); + final List<SessionInfo> result = new ArrayList<>(); synchronized (mSessions) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); @@ -529,11 +534,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @Override - public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) { + public List<SessionInfo> getMySessions(String installerPackageName, int userId) { mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions"); mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName); - final List<InstallSessionInfo> result = new ArrayList<>(); + final List<SessionInfo> result = new ArrayList<>(); synchronized (mSessions) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); @@ -547,37 +552,26 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @Override - public void uninstall(String packageName, int flags, IPackageDeleteObserver2 observer, - int userId) { + public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) { mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall"); + final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, + statusReceiver); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) { // Sweet, call straight through! - mPm.deletePackage(packageName, observer, userId, flags); + mPm.deletePackage(packageName, adapter.getBinder(), userId, flags); } else { // Take a short detour to confirm with user final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); intent.setData(Uri.fromParts("package", packageName, null)); - intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder()); - try { - observer.onUserActionRequired(intent); - } catch (RemoteException ignored) { - } + intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder()); + adapter.onUserActionRequired(intent); } } @Override - public void uninstallSplit(String basePackageName, String overlayName, int flags, - IPackageDeleteObserver2 observer, int userId) { - mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit"); - - // TODO: flesh out once PM has split support - throw new UnsupportedOperationException(); - } - - @Override public void setPermissionsResult(int sessionId, boolean accepted) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); @@ -636,6 +630,87 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } } + static class PackageDeleteObserverAdapter extends PackageDeleteObserver { + private final Context mContext; + private final IntentSender mTarget; + + public PackageDeleteObserverAdapter(Context context, IntentSender target) { + mContext = context; + mTarget = target; + } + + @Override + public void onUserActionRequired(Intent intent) { + final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_USER_ACTION_REQUIRED); + fillIn.putExtra(Intent.EXTRA_INTENT, intent); + try { + mTarget.sendIntent(mContext, 0, fillIn, null, null); + } catch (SendIntentException ignored) { + } + } + + @Override + public void onPackageDeleted(String basePackageName, int returnCode, String msg) { + final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageManager.deleteStatusToPublicStatus(returnCode)); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, + PackageManager.deleteStatusToString(returnCode, msg)); + fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); + try { + mTarget.sendIntent(mContext, 0, fillIn, null, null); + } catch (SendIntentException ignored) { + } + } + } + + static class PackageInstallObserverAdapter extends PackageInstallObserver { + private final Context mContext; + private final IntentSender mTarget; + + public PackageInstallObserverAdapter(Context context, IntentSender target) { + mContext = context; + mTarget = target; + } + + @Override + public void onUserActionRequired(Intent intent) { + final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_USER_ACTION_REQUIRED); + fillIn.putExtra(Intent.EXTRA_INTENT, intent); + try { + mTarget.sendIntent(mContext, 0, fillIn, null, null); + } catch (SendIntentException ignored) { + } + } + + @Override + public void onPackageInstalled(String basePackageName, int returnCode, String msg, + Bundle extras) { + final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageManager.installStatusToPublicStatus(returnCode)); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, + PackageManager.installStatusToString(returnCode, msg)); + fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); + if (extras != null) { + final String existing = extras.getString( + PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); + if (!TextUtils.isEmpty(existing)) { + fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAMES, new String[] { + existing }); + } + } + try { + mTarget.sendIntent(mContext, 0, fillIn, null, null); + } catch (SendIntentException ignored) { + } + } + } + private static class Callbacks extends Handler { private static final int MSG_SESSION_CREATED = 1; private static final int MSG_SESSION_OPENED = 2; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index a3184f0..5ef24f2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -25,13 +25,15 @@ import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_WRONLY; +import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; -import android.content.pm.InstallSessionInfo; -import android.content.pm.InstallSessionParams; import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; @@ -59,6 +61,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; +import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; import libcore.io.Libcore; @@ -81,13 +84,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // TODO: treat INHERIT_EXISTING as installExistingPackage() private final PackageInstallerService.InternalCallback mCallback; + private final Context mContext; private final PackageManagerService mPm; private final Handler mHandler; final int sessionId; final int userId; final String installerPackageName; - final InstallSessionParams params; + final SessionParams params; final long createdMillis; final File sessionStageDir; @@ -159,10 +163,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }; public PackageInstallerSession(PackageInstallerService.InternalCallback callback, - PackageManagerService pm, Looper looper, int sessionId, int userId, - String installerPackageName, InstallSessionParams params, long createdMillis, + Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, + String installerPackageName, SessionParams params, long createdMillis, File sessionStageDir, boolean sealed) { mCallback = callback; + mContext = context; mPm = pm; mHandler = new Handler(looper, mHandlerCallback); @@ -188,8 +193,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { computeProgressLocked(); } - public InstallSessionInfo generateInfo() { - final InstallSessionInfo info = new InstallSessionInfo(); + public SessionInfo generateInfo() { + final SessionInfo info = new SessionInfo(); info.sessionId = sessionId; info.installerPackageName = installerPackageName; @@ -246,8 +251,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @Override - public String[] list() { - assertNotSealed("list"); + public String[] getNames() { + assertNotSealed("getNames"); return sessionStageDir.list(); } @@ -337,9 +342,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @Override - public void commit(IPackageInstallObserver2 observer) { - Preconditions.checkNotNull(observer); - mHandler.obtainMessage(MSG_COMMIT, observer).sendToTarget(); + public void commit(IntentSender statusReceiver) { + Preconditions.checkNotNull(statusReceiver); + + final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, + statusReceiver); + mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); } private void commitLocked() throws PackageManagerException { @@ -385,7 +393,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Inherit any packages and native libraries from existing install that // haven't been overridden. - if (params.mode == InstallSessionParams.MODE_INHERIT_EXISTING) { + if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { spliceExistingFilesIntoStage(); } @@ -396,7 +404,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // We've reached point of no return; call into PMS to install the stage. // Regardless of success or failure we always destroy session. - final IPackageInstallObserver2 remoteObserver = mRemoteObserver; final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) { @@ -488,7 +495,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // currently relying on PMS to do this. // TODO: teach about compatible upgrade keysets. - if (params.mode == InstallSessionParams.MODE_FULL_INSTALL) { + if (params.mode == SessionParams.MODE_FULL_INSTALL) { // Full installs must include a base package if (!seenSplits.contains(null)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 89bd1d4..c1f7e85 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -107,12 +107,12 @@ import android.content.pm.IPackageInstaller; import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; -import android.content.pm.InstallSessionParams; import android.content.pm.InstrumentationInfo; import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageParser.ActivityIntentInfo; @@ -1394,16 +1394,27 @@ public class PackageManagerService extends IPackageManager.Stub { * list of process files because dexopt will have been run * if necessary during zygote startup. */ - String bootClassPath = System.getProperty("java.boot.class.path"); + final String bootClassPath = System.getenv("BOOTCLASSPATH"); + final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); + if (bootClassPath != null) { - String[] paths = splitString(bootClassPath, ':'); - for (int i=0; i<paths.length; i++) { - alreadyDexOpted.add(paths[i]); + String[] bootClassPathElements = splitString(bootClassPath, ':'); + for (String element : bootClassPathElements) { + alreadyDexOpted.add(element); } } else { Slog.w(TAG, "No BOOTCLASSPATH found!"); } + if (systemServerClassPath != null) { + String[] systemServerClassPathElements = splitString(systemServerClassPath, ':'); + for (String element : systemServerClassPathElements) { + alreadyDexOpted.add(element); + } + } else { + Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); + } + boolean didDexOptLibraryOrTool = false; final List<String> allInstructionSets = getAllInstructionSets(); @@ -2920,7 +2931,8 @@ public class PackageManagerService extends IPackageManager.Stub { findPreferredActivity(intent, resolvedType, flags, query, 0, false, true, false, userId); // Add the new activity as the last chosen for this filter - addPreferredActivityInternal(filter, match, null, activity, false, userId); + addPreferredActivityInternal(filter, match, null, activity, false, userId, + "Setting last chosen"); } @Override @@ -7835,7 +7847,7 @@ public class PackageManagerService extends IPackageManager.Stub { } void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer, - InstallSessionParams params, String installerPackageName, int installerUid, + PackageInstaller.SessionParams params, String installerPackageName, int installerUid, UserHandle user) { final VerificationParams verifParams = new VerificationParams(null, params.originatingUri, params.referrerUri, installerUid, null); @@ -11462,11 +11474,13 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { - addPreferredActivityInternal(filter, match, set, activity, true, userId); + addPreferredActivityInternal(filter, match, set, activity, true, userId, + "Adding preferred"); } private void addPreferredActivityInternal(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity, boolean always, int userId) { + ComponentName[] set, ComponentName activity, boolean always, int userId, + String opname) { // writer int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, "add preferred activity"); @@ -11488,10 +11502,11 @@ public class PackageManagerService extends IPackageManager.Stub { android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :"); + PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId); + Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user " + + userId + ":"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); - mSettings.editPreferredActivitiesLPw(userId).addFilter( - new PreferredActivity(filter, match, set, activity, always)); + pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); mSettings.writePackageRestrictionsLPr(userId); } } @@ -11514,7 +11529,6 @@ public class PackageManagerService extends IPackageManager.Stub { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, "replace preferred activity"); - final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) @@ -11529,30 +11543,61 @@ public class PackageManagerService extends IPackageManager.Stub { android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - PreferredIntentResolver pir = mSettings.mPreferredActivities.get(callingUserId); + PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); if (pir != null) { - Intent intent = new Intent(filter.getAction(0)).addCategory(filter.getCategory(0)); - if (filter.countDataSchemes() == 1) { - Uri.Builder builder = new Uri.Builder(); - builder.scheme(filter.getDataScheme(0)); - intent.setData(builder.build()); - } - List<PreferredActivity> matches = pir.queryIntent( - intent, null, true, callingUserId); + // Get all of the existing entries that exactly match this filter. + ArrayList<PreferredActivity> existing = pir.findFilters(filter); + if (existing != null && existing.size() == 1) { + PreferredActivity cur = existing.get(0); + if (DEBUG_PREFERRED) { + Slog.i(TAG, "Checking replace of preferred:"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + if (!cur.mPref.mAlways) { + Slog.i(TAG, " -- CUR; not mAlways!"); + } else { + Slog.i(TAG, " -- CUR: mMatch=" + cur.mPref.mMatch); + Slog.i(TAG, " -- CUR: mSet=" + + Arrays.toString(cur.mPref.mSetComponents)); + Slog.i(TAG, " -- CUR: mComponent=" + cur.mPref.mShortComponent); + Slog.i(TAG, " -- NEW: mMatch=" + + (match&IntentFilter.MATCH_CATEGORY_MASK)); + Slog.i(TAG, " -- CUR: mSet=" + Arrays.toString(set)); + Slog.i(TAG, " -- CUR: mComponent=" + activity.flattenToShortString()); + } + } + if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity) + && cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK) + && cur.mPref.sameSet(set)) { + if (DEBUG_PREFERRED) { + Slog.i(TAG, "Replacing with same preferred activity " + + cur.mPref.mShortComponent + " for user " + + userId + ":"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + } else { + Slog.i(TAG, "Replacing with same preferred activity " + + cur.mPref.mShortComponent + " for user " + + userId); + } + return; + } + } + if (DEBUG_PREFERRED) { - Slog.i(TAG, matches.size() + " preferred matches for " + intent); + Slog.i(TAG, existing.size() + " existing preferred matches for:"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - for (int i = 0; i < matches.size(); i++) { - PreferredActivity pa = matches.get(i); + for (int i = 0; i < existing.size(); i++) { + PreferredActivity pa = existing.get(i); if (DEBUG_PREFERRED) { - Slog.i(TAG, "Removing preferred activity " + Slog.i(TAG, "Removing existing preferred activity " + pa.mPref.mComponent + ":"); - filter.dump(new LogPrinter(Log.INFO, TAG), " "); + pa.dump(new LogPrinter(Log.INFO, TAG), " "); } pir.removeFilter(pa); } } - addPreferredActivityInternal(filter, match, set, activity, true, callingUserId); + addPreferredActivityInternal(filter, match, set, activity, true, userId, + "Replacing preferred"); } } diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index f437372..69c1909 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -44,10 +44,10 @@ public class PreferredComponent { // Whether this is to be the one that's always chosen. If false, it's the most recently chosen. public boolean mAlways; - private final String[] mSetPackages; - private final String[] mSetClasses; - private final String[] mSetComponents; - private final String mShortComponent; + final String[] mSetPackages; + final String[] mSetClasses; + final String[] mSetComponents; + final String mShortComponent; private String mParseError; private final Callbacks mCallbacks; @@ -193,7 +193,12 @@ public class PreferredComponent { } public boolean sameSet(List<ResolveInfo> query, int priority) { - if (mSetPackages == null) return false; + if (mSetPackages == null) { + return query == null; + } + if (query == null) { + return false; + } final int NQ = query.size(); final int NS = mSetPackages.length; int numMatch = 0; @@ -215,6 +220,27 @@ public class PreferredComponent { return numMatch == NS; } + public boolean sameSet(ComponentName[] comps) { + if (mSetPackages == null) return false; + final int NQ = comps.length; + final int NS = mSetPackages.length; + int numMatch = 0; + for (int i=0; i<NQ; i++) { + ComponentName cn = comps[i]; + boolean good = false; + for (int j=0; j<NS; j++) { + if (mSetPackages[j].equals(cn.getPackageName()) + && mSetClasses[j].equals(cn.getClassName())) { + numMatch++; + good = true; + break; + } + } + if (!good) return false; + } + return numMatch == NS; + } + public void dump(PrintWriter out, String prefix, Object ident) { out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(ident))); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 2b4a24a..46497242 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -406,7 +406,6 @@ final class Notifier { private void sendNextBroadcast() { final int powerState; - final int goToSleepReason; synchronized (mLock) { if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) { // Broadcasted power state is unknown. Send wake up. diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index f0fd911..9734bd4 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -56,10 +56,9 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.SystemService; +import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; -import android.os.Parcel; -import android.os.ServiceManager; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; import android.util.EventLog; @@ -709,7 +708,6 @@ public final class PowerManagerService extends com.android.server.SystemService if (mLowPowerModeEnabled != lowPowerModeEnabled) { mLowPowerModeEnabled = lowPowerModeEnabled; powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0); - setSurfaceFlingerLowPowerMode(lowPowerModeEnabled ? 1 : 0); mLowPowerModeEnabled = lowPowerModeEnabled; BackgroundThread.getHandler().post(new Runnable() { @Override @@ -820,17 +818,12 @@ public final class PowerManagerService extends com.android.server.SystemService + " [" + wakeLock.mTag + "], flags=0x" + Integer.toHexString(flags)); } - mWakeLocks.remove(index); - notifyWakeLockReleasedLocked(wakeLock); - wakeLock.mLock.unlinkToDeath(wakeLock, 0); - if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) { mRequestWaitForNegativeProximity = true; } - applyWakeLockFlagsOnReleaseLocked(wakeLock); - mDirty |= DIRTY_WAKE_LOCKS; - updatePowerStateLocked(); + wakeLock.mLock.unlinkToDeath(wakeLock, 0); + removeWakeLockLocked(wakeLock, index); } } @@ -846,15 +839,19 @@ public final class PowerManagerService extends com.android.server.SystemService return; } - mWakeLocks.remove(index); - notifyWakeLockReleasedLocked(wakeLock); - - applyWakeLockFlagsOnReleaseLocked(wakeLock); - mDirty |= DIRTY_WAKE_LOCKS; - updatePowerStateLocked(); + removeWakeLockLocked(wakeLock, index); } } + private void removeWakeLockLocked(WakeLock wakeLock, int index) { + mWakeLocks.remove(index); + notifyWakeLockReleasedLocked(wakeLock); + + applyWakeLockFlagsOnReleaseLocked(wakeLock); + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); + } + private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) { if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0 && isScreenLock(wakeLock)) { @@ -977,21 +974,26 @@ public final class PowerManagerService extends com.android.server.SystemService return false; } - mNotifier.onUserActivity(event, uid); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "userActivity"); + try { + mNotifier.onUserActivity(event, uid); - if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) { - if (eventTime > mLastUserActivityTimeNoChangeLights - && eventTime > mLastUserActivityTime) { - mLastUserActivityTimeNoChangeLights = eventTime; - mDirty |= DIRTY_USER_ACTIVITY; - return true; - } - } else { - if (eventTime > mLastUserActivityTime) { - mLastUserActivityTime = eventTime; - mDirty |= DIRTY_USER_ACTIVITY; - return true; + if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) { + if (eventTime > mLastUserActivityTimeNoChangeLights + && eventTime > mLastUserActivityTime) { + mLastUserActivityTimeNoChangeLights = eventTime; + mDirty |= DIRTY_USER_ACTIVITY; + return true; + } + } else { + if (eventTime > mLastUserActivityTime) { + mLastUserActivityTime = eventTime; + mDirty |= DIRTY_USER_ACTIVITY; + return true; + } } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } return false; } @@ -1014,25 +1016,30 @@ public final class PowerManagerService extends com.android.server.SystemService return false; } - switch (mWakefulness) { - case WAKEFULNESS_ASLEEP: - Slog.i(TAG, "Waking up from sleep (uid " + uid +")..."); - break; - case WAKEFULNESS_DREAMING: - Slog.i(TAG, "Waking up from dream (uid " + uid +")..."); - break; - case WAKEFULNESS_DOZING: - Slog.i(TAG, "Waking up from dozing (uid " + uid +")..."); - break; - } + Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); + try { + switch (mWakefulness) { + case WAKEFULNESS_ASLEEP: + Slog.i(TAG, "Waking up from sleep (uid " + uid +")..."); + break; + case WAKEFULNESS_DREAMING: + Slog.i(TAG, "Waking up from dream (uid " + uid +")..."); + break; + case WAKEFULNESS_DOZING: + Slog.i(TAG, "Waking up from dozing (uid " + uid +")..."); + break; + } - mLastWakeTime = eventTime; - mDirty |= DIRTY_WAKEFULNESS; - mWakefulness = WAKEFULNESS_AWAKE; - setInteractiveStateLocked(true, 0); + mLastWakeTime = eventTime; + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_AWAKE; + setInteractiveStateLocked(true, 0); - userActivityNoUpdateLocked( - eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); + userActivityNoUpdateLocked( + eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } return true; } @@ -1060,53 +1067,58 @@ public final class PowerManagerService extends com.android.server.SystemService return false; } - switch (reason) { - case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: - Slog.i(TAG, "Going to sleep due to device administration policy " - + "(uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: - Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH: - Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON: - Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_HDMI: - Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")..."); - break; - default: - Slog.i(TAG, "Going to sleep by application request (uid " + uid +")..."); - reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION; - break; - } - - mLastSleepTime = eventTime; - mDirty |= DIRTY_WAKEFULNESS; - mWakefulness = WAKEFULNESS_DOZING; - mSandmanSummoned = true; - setInteractiveStateLocked(false, reason); - - // Report the number of wake locks that will be cleared by going to sleep. - int numWakeLocksCleared = 0; - final int numWakeLocks = mWakeLocks.size(); - for (int i = 0; i < numWakeLocks; i++) { - final WakeLock wakeLock = mWakeLocks.get(i); - switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { - case PowerManager.FULL_WAKE_LOCK: - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - case PowerManager.SCREEN_DIM_WAKE_LOCK: - numWakeLocksCleared += 1; + Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep"); + try { + switch (reason) { + case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: + Slog.i(TAG, "Going to sleep due to device administration policy " + + "(uid " + uid +")..."); + break; + case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: + Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")..."); + break; + case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH: + Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")..."); + break; + case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON: + Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")..."); + break; + case PowerManager.GO_TO_SLEEP_REASON_HDMI: + Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")..."); + break; + default: + Slog.i(TAG, "Going to sleep by application request (uid " + uid +")..."); + reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION; break; } - } - EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared); - // Skip dozing if requested. - if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { - reallyGoToSleepNoUpdateLocked(eventTime, uid); + mLastSleepTime = eventTime; + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_DOZING; + mSandmanSummoned = true; + setInteractiveStateLocked(false, reason); + + // Report the number of wake locks that will be cleared by going to sleep. + int numWakeLocksCleared = 0; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + numWakeLocksCleared += 1; + break; + } + } + EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared); + + // Skip dozing if requested. + if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { + reallyGoToSleepNoUpdateLocked(eventTime, uid); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } @@ -1129,12 +1141,17 @@ public final class PowerManagerService extends com.android.server.SystemService return false; } - Slog.i(TAG, "Nap time (uid " + uid +")..."); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap"); + try { + Slog.i(TAG, "Nap time (uid " + uid +")..."); - mDirty |= DIRTY_WAKEFULNESS; - mWakefulness = WAKEFULNESS_DREAMING; - mSandmanSummoned = true; - setInteractiveStateLocked(true, 0); + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_DREAMING; + mSandmanSummoned = true; + setInteractiveStateLocked(true, 0); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } return true; } @@ -1150,11 +1167,16 @@ public final class PowerManagerService extends com.android.server.SystemService return false; } - Slog.i(TAG, "Sleeping (uid " + uid +")..."); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep"); + try { + Slog.i(TAG, "Sleeping (uid " + uid +")..."); - mDirty |= DIRTY_WAKEFULNESS; - mWakefulness = WAKEFULNESS_ASLEEP; - setInteractiveStateLocked(false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_ASLEEP; + setInteractiveStateLocked(false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } return true; } @@ -1191,40 +1213,45 @@ public final class PowerManagerService extends com.android.server.SystemService Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked"); } - // Phase 0: Basic state updates. - updateIsPoweredLocked(mDirty); - updateStayOnLocked(mDirty); - - // Phase 1: Update wakefulness. - // Loop because the wake lock and user activity computations are influenced - // by changes in wakefulness. - final long now = SystemClock.uptimeMillis(); - int dirtyPhase2 = 0; - for (;;) { - int dirtyPhase1 = mDirty; - dirtyPhase2 |= dirtyPhase1; - mDirty = 0; + Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState"); + try { + // Phase 0: Basic state updates. + updateIsPoweredLocked(mDirty); + updateStayOnLocked(mDirty); - updateWakeLockSummaryLocked(dirtyPhase1); - updateUserActivitySummaryLocked(now, dirtyPhase1); - if (!updateWakefulnessLocked(dirtyPhase1)) { - break; + // Phase 1: Update wakefulness. + // Loop because the wake lock and user activity computations are influenced + // by changes in wakefulness. + final long now = SystemClock.uptimeMillis(); + int dirtyPhase2 = 0; + for (;;) { + int dirtyPhase1 = mDirty; + dirtyPhase2 |= dirtyPhase1; + mDirty = 0; + + updateWakeLockSummaryLocked(dirtyPhase1); + updateUserActivitySummaryLocked(now, dirtyPhase1); + if (!updateWakefulnessLocked(dirtyPhase1)) { + break; + } } - } - // Phase 2: Update dreams and display power state. - updateDreamLocked(dirtyPhase2); - updateDisplayPowerStateLocked(dirtyPhase2); + // Phase 2: Update dreams and display power state. + updateDreamLocked(dirtyPhase2); + updateDisplayPowerStateLocked(dirtyPhase2); - // Phase 3: Send notifications, if needed. - if (mDisplayReady) { - finishInteractiveStateChangeLocked(); - } + // Phase 3: Send notifications, if needed. + if (mDisplayReady) { + finishInteractiveStateChangeLocked(); + } - // Phase 4: Update suspend blocker. - // Because we might release the last suspend blocker here, we need to make sure - // we finished everything else first! - updateSuspendBlockerLocked(); + // Phase 4: Update suspend blocker. + // Because we might release the last suspend blocker here, we need to make sure + // we finished everything else first! + updateSuspendBlockerLocked(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } } /** @@ -1995,7 +2022,12 @@ public final class PowerManagerService extends com.android.server.SystemService Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable); } mHalAutoSuspendModeEnabled = enable; - nativeSetAutoSuspend(enable); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")"); + try { + nativeSetAutoSuspend(enable); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } } } @@ -2005,7 +2037,12 @@ public final class PowerManagerService extends com.android.server.SystemService Slog.d(TAG, "Setting HAL interactive mode to " + enable); } mHalInteractiveModeEnabled = enable; - nativeSetInteractive(enable); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")"); + try { + nativeSetInteractive(enable); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } } } @@ -2200,21 +2237,6 @@ public final class PowerManagerService extends com.android.server.SystemService nativeSendPowerHint(hintId, data); } - private static void setSurfaceFlingerLowPowerMode(int enabled) { - try { - final IBinder flinger = ServiceManager.getService("SurfaceFlinger"); - if (flinger != null) { - final Parcel data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - data.writeInt(enabled); - flinger.transact(1016, data, null, 0); - data.recycle(); - } - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to reduce refresh rate", ex); - } - } - /** * Low-level function turn the device off immediately, without trying * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. @@ -2607,20 +2629,23 @@ public final class PowerManagerService extends com.android.server.SystemService private final class SuspendBlockerImpl implements SuspendBlocker { private final String mName; + private final String mTraceName; private int mReferenceCount; public SuspendBlockerImpl(String name) { mName = name; + mTraceName = "SuspendBlocker (" + name + ")"; } @Override protected void finalize() throws Throwable { try { if (mReferenceCount != 0) { - Log.wtf(TAG, "Suspend blocker \"" + mName + Slog.wtf(TAG, "Suspend blocker \"" + mName + "\" was finalized without being released!"); mReferenceCount = 0; nativeReleaseSuspendBlocker(mName); + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0); } } finally { super.finalize(); @@ -2635,6 +2660,7 @@ public final class PowerManagerService extends com.android.server.SystemService if (DEBUG_SPEW) { Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\"."); } + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0); nativeAcquireSuspendBlocker(mName); } } @@ -2649,8 +2675,9 @@ public final class PowerManagerService extends com.android.server.SystemService Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\"."); } nativeReleaseSuspendBlocker(mName); + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0); } else if (mReferenceCount < 0) { - Log.wtf(TAG, "Suspend blocker \"" + mName + Slog.wtf(TAG, "Suspend blocker \"" + mName + "\" was released without being acquired!", new Throwable()); mReferenceCount = 0; } @@ -2666,6 +2693,8 @@ public final class PowerManagerService extends com.android.server.SystemService } private final class ScreenOnBlockerImpl implements ScreenOnBlocker { + private static final String TRACE_NAME = "ScreenOnBlocker"; + private int mNestCount; public boolean isHeld() { @@ -2678,9 +2707,12 @@ public final class PowerManagerService extends com.android.server.SystemService public void acquire() { synchronized (this) { mNestCount += 1; - if (DEBUG) { + if (DEBUG || true) { Slog.d(TAG, "Screen on blocked: mNestCount=" + mNestCount); } + if (mNestCount == 1) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_NAME, 0); + } } } @@ -2688,16 +2720,16 @@ public final class PowerManagerService extends com.android.server.SystemService public void release() { synchronized (this) { mNestCount -= 1; - if (mNestCount < 0) { - Log.wtf(TAG, "Screen on blocker was released without being acquired!", - new Throwable()); - mNestCount = 0; - } if (mNestCount == 0) { + if (DEBUG || true) { + Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount); + } mHandler.sendEmptyMessage(MSG_SCREEN_ON_BLOCKER_RELEASED); - } - if (DEBUG) { - Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount); + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_NAME, 0); + } else if (mNestCount < 0) { + Slog.wtf(TAG, "Screen on blocker was released without being acquired!", + new Throwable()); + mNestCount = 0; } } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 21905f0..297dacf 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -48,9 +48,7 @@ import java.util.Map; * A note on locking: We rely on the fact that calls onto mBar are oneway or * if they are local, that they just enqueue messages to not deadlock. */ -public class StatusBarManagerService extends IStatusBarService.Stub - implements WindowManagerService.OnHardKeyboardStatusChangeListener -{ +public class StatusBarManagerService extends IStatusBarService.Stub { private static final String TAG = "StatusBarManagerService"; private static final boolean SPEW = false; @@ -95,7 +93,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub public StatusBarManagerService(Context context, WindowManagerService windowManager) { mContext = context; mWindowManager = windowManager; - mWindowManager.setOnHardKeyboardStatusChangeListener(this); final Resources res = context.getResources(); mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons)); @@ -394,29 +391,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub } @Override - public void setHardKeyboardEnabled(final boolean enabled) { - mHandler.post(new Runnable() { - public void run() { - mWindowManager.setHardKeyboardEnabled(enabled); - } - }); - } - - @Override - public void onHardKeyboardStatusChange(final boolean available, final boolean enabled) { - mHandler.post(new Runnable() { - public void run() { - if (mBar != null) { - try { - mBar.setHardKeyboardStatus(available, enabled); - } catch (RemoteException ex) { - } - } - } - }); - } - - @Override public void toggleRecentApps() { if (mBar != null) { try { @@ -510,11 +484,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub switches[2] = mMenuVisible ? 1 : 0; switches[3] = mImeWindowVis; switches[4] = mImeBackDisposition; - switches[7] = mShowImeSwitcher ? 1 : 0; + switches[5] = mShowImeSwitcher ? 1 : 0; binders.add(mImeToken); } - switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0; - switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0; } /** diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java index 05a2bde..fcfaaea 100644 --- a/services/core/java/com/android/server/tv/PersistentDataStore.java +++ b/services/core/java/com/android/server/tv/PersistentDataStore.java @@ -45,6 +45,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -69,7 +70,8 @@ final class PersistentDataStore { // The atomic file used to safely read or write the file. private final AtomicFile mAtomicFile; - private final List<TvContentRating> mBlockedRatings = new ArrayList<TvContentRating>(); + private final List<TvContentRating> mBlockedRatings = + Collections.synchronizedList(new ArrayList<TvContentRating>()); private boolean mBlockedRatingsChanged; @@ -107,9 +109,11 @@ final class PersistentDataStore { public boolean isRatingBlocked(TvContentRating rating) { loadIfNeeded(); - for (TvContentRating blcokedRating : mBlockedRatings) { - if (rating.contains(blcokedRating)) { - return true; + synchronized (mBlockedRatings) { + for (TvContentRating blcokedRating : mBlockedRatings) { + if (rating.contains(blcokedRating)) { + return true; + } } } return false; @@ -271,10 +275,12 @@ final class PersistentDataStore { serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE); serializer.startTag(null, TAG_BLOCKED_RATINGS); - for (TvContentRating rating : mBlockedRatings) { - serializer.startTag(null, TAG_RATING); - serializer.attribute(null, ATTR_STRING, rating.flattenToString()); - serializer.endTag(null, TAG_RATING); + synchronized (mBlockedRatings) { + for (TvContentRating rating : mBlockedRatings) { + serializer.startTag(null, TAG_RATING); + serializer.attribute(null, ATTR_STRING, rating.flattenToString()); + serializer.endTag(null, TAG_RATING); + } } serializer.endTag(null, TAG_BLOCKED_RATINGS); serializer.startTag(null, TAG_PARENTAL_CONTROLS); diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 78ea6c9..ae9ae13 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -285,7 +285,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { return -1; } - public void addHdmiTvInput(int logicalAddress, TvInputInfo info) { + public void addHdmiTvInput(int id, TvInputInfo info) { if (info.getType() != TvInputInfo.TYPE_HDMI) { throw new IllegalArgumentException("info (" + info + ") has non-HDMI type."); } @@ -295,13 +295,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (parentIndex < 0) { throw new IllegalArgumentException("info (" + info + ") has invalid parentId."); } - String oldInputId = mHdmiInputIdMap.get(logicalAddress); + String oldInputId = mHdmiInputIdMap.get(id); if (oldInputId != null) { Slog.w(TAG, "Trying to override previous registration: old = " - + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = " - + info + ":" + logicalAddress); + + mInputMap.get(oldInputId) + ":" + id + ", new = " + + info + ":" + id); } - mHdmiInputIdMap.put(logicalAddress, info.getId()); + mHdmiInputIdMap.put(id, info.getId()); mInputMap.put(info.getId(), info); } } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 3b5e79d..88ce860 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1223,7 +1223,7 @@ public final class TvInputManagerService extends SystemService { try { getSessionLocked(sessionToken, callingUid, resolvedUserId).tune( channelUri, params); - if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) { + if (TvContract.isChannelUriForPassthroughInput(channelUri)) { // Do not log the watch history for passthrough inputs. return; } @@ -1962,11 +1962,11 @@ public final class TvInputManagerService extends SystemService { } @Override - public void addHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) { + public void addHdmiTvInput(int id, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); synchronized (mLock) { - mTvInputHardwareManager.addHdmiTvInput(logicalAddress, inputInfo); + mTvInputHardwareManager.addHdmiTvInput(id, inputInfo); addTvInputLocked(inputInfo); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2295656..112972f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -537,7 +537,7 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); boolean mHardKeyboardAvailable; - boolean mHardKeyboardEnabled; + boolean mShowImeWithHardKeyboard; OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>(); @@ -7015,11 +7015,11 @@ public class WindowManagerService extends IWindowManager.Stub boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS; if (hardKeyboardAvailable != mHardKeyboardAvailable) { mHardKeyboardAvailable = hardKeyboardAvailable; - mHardKeyboardEnabled = hardKeyboardAvailable; + mShowImeWithHardKeyboard = !hardKeyboardAvailable; mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); } - if (!mHardKeyboardEnabled) { + if (mShowImeWithHardKeyboard) { config.keyboard = Configuration.KEYBOARD_NOKEYS; } @@ -7039,16 +7039,16 @@ public class WindowManagerService extends IWindowManager.Stub } } - public boolean isHardKeyboardEnabled() { + public boolean isShowImeWithHardKeyboardEnabled() { synchronized (mWindowMap) { - return mHardKeyboardEnabled; + return mShowImeWithHardKeyboard; } } - public void setHardKeyboardEnabled(boolean enabled) { + public void setShowImeWithHardKeyboard(boolean enabled) { synchronized (mWindowMap) { - if (mHardKeyboardEnabled != enabled) { - mHardKeyboardEnabled = enabled; + if (mShowImeWithHardKeyboard != enabled) { + mShowImeWithHardKeyboard = enabled; mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } } @@ -7062,15 +7062,15 @@ public class WindowManagerService extends IWindowManager.Stub } void notifyHardKeyboardStatusChange() { - final boolean available, enabled; + final boolean available, showImeWithHardKeyboard; final OnHardKeyboardStatusChangeListener listener; synchronized (mWindowMap) { listener = mHardKeyboardStatusChangeListener; available = mHardKeyboardAvailable; - enabled = mHardKeyboardEnabled; + showImeWithHardKeyboard = mShowImeWithHardKeyboard; } if (listener != null) { - listener.onHardKeyboardStatusChange(available, enabled); + listener.onHardKeyboardStatusChange(available, showImeWithHardKeyboard); } } @@ -11053,7 +11053,7 @@ public class WindowManagerService extends IWindowManager.Stub } public interface OnHardKeyboardStatusChangeListener { - public void onHardKeyboardStatusChange(boolean available, boolean enabled); + public void onHardKeyboardStatusChange(boolean available, boolean showIme); } void debugLayoutRepeats(final String msg, int pendingLayoutChanges) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 61c50d6..7bf090a 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1211,6 +1211,12 @@ class WindowStateAnimator { || w.mDecorFrame.isEmpty()) { // The universe background isn't cropped, nor windows without policy decor. w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height()); + } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.mAnimating) { + // If we're animating, the wallpaper crop should only be updated at the end of the + // animation. + mTmpClipRect.set(w.mSystemDecorRect); + applyDecorRect(w.mDecorFrame); + w.mSystemDecorRect.union(mTmpClipRect); } else { // Crop to the system decor specified by policy. applyDecorRect(w.mDecorFrame); diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index ce2ca9b..39b70a8 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -41,6 +41,8 @@ int register_android_server_hdmi_HdmiCecController(JNIEnv* env); int register_android_server_hdmi_HdmiMhlController(JNIEnv* env); int register_android_server_tv_TvInputHal(JNIEnv* env); int register_android_server_PersistentDataBlockService(JNIEnv* env); +int register_android_server_fingerprint_FingerprintService(JNIEnv* env); +int register_android_server_Watchdog(JNIEnv* env); }; using namespace android; @@ -77,6 +79,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_hdmi_HdmiMhlController(env); register_android_server_tv_TvInputHal(env); register_android_server_PersistentDataBlockService(env); + register_android_server_fingerprint_FingerprintService(env); + register_android_server_Watchdog(env); return JNI_VERSION_1_4; } |