diff --git a/Android.mk b/Android.mk index 3d52f44f3f93ff43413962cc4165cf8a3235d448..1db4365c552f8215e29950e2e8af8d5e14c9178c 100644 --- a/Android.mk +++ b/Android.mk @@ -264,8 +264,9 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \ telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ telephony/java/com/android/internal/telephony/ITelephony.aidl \ - telephony/java/com/android/internal/telephony/ISms.aidl \ + telephony/java/com/android/internal/telephony/ITelephonyListener.aidl \ telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \ + telephony/java/com/android/internal/telephony/ISms.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ @@ -542,8 +543,7 @@ framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \ framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \ frameworks/base/docs/knowntags.txt -sample_dir := development/samples/browseable -new_sample_dir := developers/samples/android +samples_dir := development/samples/browseable # Whitelist of valid groups, used for default TOC grouping. Each sample must # belong to one (and only one) group. Assign samples to groups by setting @@ -553,166 +553,13 @@ sample_groups := -samplegroup Background \ -samplegroup Content \ -samplegroup Input \ -samplegroup Media \ + -samplegroup RenderScript \ -samplegroup Security \ + -samplegroup Sensors \ -samplegroup Testing \ -samplegroup UI \ -samplegroup Views -# the list here should match the list of samples included in the sdk samples package -# (see development/build/sdk.atree) -# remove htmlified samples for now -- samples are still available through the SDK -web_docs_sample_code_flags := \ - -hdf android.hasSamples 1 \ - -samplecode $(sample_dir)/BasicAccessibility \ - samples/BasicAccessibility "" \ - -samplecode $(sample_dir)/HorizontalPaging \ - samples/HorizontalPaging "" \ - -samplecode $(sample_dir)/ShareActionProvider \ - samples/ShareActionProvider "" \ - -samplecode $(sample_dir)/Styled \ - samples/Styled "" \ - -samplecode $(sample_dir)/BasicAndroidKeyStore \ - samples/BasicAndroidKeyStore "" \ - -samplecode $(sample_dir)/Basic \ - samples/Basic "" \ - -samplecode $(sample_dir)/ImmersiveMode \ - samples/ImmersiveMode "" \ - -samplecode $(sample_dir)/repeatingAlarm \ - samples/repeatingAlarm "" \ - -samplecode $(sample_dir)/TextLinkify \ - samples/TextLinkify "" \ - -samplecode $(sample_dir)/BasicMediaRouter \ - samples/BasicMediaRouter "" \ - -samplecode $(sample_dir)/BasicMultitouch \ - samples/BasicMultitouch "" \ - -samplecode $(sample_dir)/TextSwitcher \ - samples/TextSwitcher "" \ - -samplecode $(sample_dir)/ActivityInstrumentation \ - samples/ActivityInstrumentation "" \ - -samplecode $(sample_dir)/BorderlessButtons \ - samples/BorderlessButtons "" \ - -samplecode $(sample_dir)/BasicNotifications \ - samples/BasicNotifications "" \ - -samplecode $(sample_dir)/AdvancedImmersiveMode \ - samples/AdvancedImmersiveMode "" \ - -samplecode $(sample_dir)/BluetoothLeGatt \ - samples/BluetoothLeGatt "" \ - -samplecode $(sample_dir)/NetworkConnect \ - samples/NetworkConnect "" \ - -samplecode $(sample_dir)/BasicNetworking \ - samples/BasicNetworking "" \ - -samplecode $(sample_dir)/BasicMediaDecoder \ - samples/BasicMediaDecoder "" \ - -samplecode $(sample_dir)/BasicImmersiveMode \ - samples/BasicImmersiveMode "" \ - -samplecode $(sample_dir)/CustomChoiceList \ - samples/CustomChoiceList "" \ - -samplecode $(sample_dir)/BasicContactables \ - samples/BasicContactables "" \ - -samplecode $(sample_dir)/BasicGestureDetect \ - samples/BasicGestureDetect "" \ - -samplecode $(sample_dir)/DoneBar \ - samples/DoneBar "" \ - -samplecode $(sample_dir)/ListPopupMenu \ - samples/ListPopupMenu "" \ - -samplecode $(sample_dir)/AppRestrictions \ - samples/AppRestrictions "" \ - -samplecode $(sample_dir)/CustomNotifications \ - samples/CustomNotifications "" \ - -samplecode $(sample_dir)/BasicSyncAdapter \ - samples/BasicSyncAdapter "" \ - -samplecode $(sample_dir)/StorageClient \ - samples/StorageClient "" \ - -samplecode $(sample_dir)/StorageProvider \ - samples/StorageProvider "" -# -samplecode $(sample_dir)/AndroidBeamDemo \ -# samples/AndroidBeamDemo "Android Beam Demo" \ -# -samplecode $(sample_dir)/ApiDemos \ -# samples/ApiDemos "API Demos" \ -# -samplecode $(sample_dir)/Support4Demos \ -# samples/Support4Demos "API 4+ Support Demos" \ -# -samplecode $(sample_dir)/Support13Demos \ -# samples/Support13Demos "API 13+ Support Demos" \ -# -samplecode $(sample_dir)/BackupRestore \ -# samples/BackupRestore "Backup and Restore" \ -# -samplecode $(sample_dir)/BluetoothChat \ -# samples/BluetoothChat "Bluetooth Chat" \ -# -samplecode $(sample_dir)/BusinessCard \ -# samples/BusinessCard "Business Card" \ -# -samplecode $(sample_dir)/ContactManager \ -# samples/ContactManager "Contact Manager" \ -# -samplecode $(sample_dir)/CubeLiveWallpaper \ -# samples/CubeLiveWallpaper "Cube Live Wallpaper" \ -# -samplecode $(sample_dir)/Home \ -# samples/Home "Home" \ -# -samplecode $(sample_dir)/HoneycombGallery \ -# samples/HoneycombGallery "Honeycomb Gallery" \ -# -samplecode $(sample_dir)/JetBoy \ -# samples/JetBoy "JetBoy" \ -# -samplecode $(sample_dir)/KeyChainDemo \ -# samples/KeyChainDemo "KeyChain Demo" \ -# -samplecode $(sample_dir)/LunarLander \ -# samples/LunarLander "Lunar Lander" \ -# -samplecode $(sample_dir)/training/ads-and-ux \ -# samples/training/ads-and-ux "Mobile Advertisement Integration" \ -# -samplecode $(sample_dir)/MultiResolution \ -# samples/MultiResolution "Multiple Resolutions" \ -# -samplecode $(sample_dir)/training/multiscreen/newsreader \ -# samples/newsreader "News Reader" \ -# -samplecode $(sample_dir)/NotePad \ -# samples/NotePad "Note Pad" \ -# -samplecode $(sample_dir)/SpellChecker/SampleSpellCheckerService \ -# samples/SpellChecker/SampleSpellCheckerService "Spell Checker Service" \ -# -samplecode $(sample_dir)/SpellChecker/HelloSpellChecker \ -# samples/SpellChecker/HelloSpellChecker "Spell Checker Client" \ -# -samplecode $(sample_dir)/SampleSyncAdapter \ -# samples/SampleSyncAdapter "Sample Sync Adapter" \ -# -samplecode $(sample_dir)/RandomMusicPlayer \ -# samples/RandomMusicPlayer "Random Music Player" \ -# -samplecode $(sample_dir)/RenderScript \ -# samples/RenderScript "RenderScript" \ -# -samplecode $(sample_dir)/SearchableDictionary \ -# samples/SearchableDictionary "Searchable Dictionary v2" \ -# -samplecode $(sample_dir)/SipDemo \ -# samples/SipDemo "SIP Demo" \ -# -samplecode $(sample_dir)/Snake \ -# samples/Snake "Snake" \ -# -samplecode $(sample_dir)/SoftKeyboard \ -# samples/SoftKeyboard "Soft Keyboard" \ -# -samplecode $(sample_dir)/Spinner \ -# samples/Spinner "Spinner" \ -# -samplecode $(sample_dir)/SpinnerTest \ -# samples/SpinnerTest "SpinnerTest" \ -# -samplecode $(sample_dir)/StackWidget \ -# samples/StackWidget "StackView Widget" \ -# -samplecode $(sample_dir)/TicTacToeLib \ -# samples/TicTacToeLib "TicTacToeLib" \ -# -samplecode $(sample_dir)/TicTacToeMain \ -# samples/TicTacToeMain "TicTacToeMain" \ -# -samplecode $(sample_dir)/ToyVpn \ -# samples/ToyVpn "Toy VPN Client" \ -# -samplecode $(sample_dir)/USB \ -# samples/USB "USB" \ -# -samplecode $(sample_dir)/WeatherListWidget \ -# samples/WeatherListWidget "Weather List Widget" \ -# -samplecode $(sample_dir)/WiFiDirectDemo \ -# samples/WiFiDirectDemo "Wi-Fi Direct Demo" \ -# -samplecode $(sample_dir)/Wiktionary \ -# samples/Wiktionary "Wiktionary" \ -# -samplecode $(sample_dir)/WiktionarySimple \ -# samples/WiktionarySimple "Wiktionary (Simplified)" \ -# -samplecode $(sample_dir)/VoiceRecognitionService \ -# samples/VoiceRecognitionService "Voice Recognition Service" \ -# -samplecode $(sample_dir)/VoicemailProviderDemo \ -# samples/VoicemailProviderDemo "Voicemail Provider Demo" \ -# -samplecode $(sample_dir)/XmlAdapters \ -# samples/XmlAdapters "XML Adapters" \ -# -samplecode $(sample_dir)/TtsEngine \ -# samples/TtsEngine "Text To Speech Engine" \ -# -samplecode $(sample_dir)/training/device-management-policy \ -# samples/training/device-management-policy "Device Management Policy" - - ## SDK version identifiers used in the published docs # major[.minor] version for current SDK. (full releases only) framework_docs_SDK_VERSION:=4.4 @@ -722,7 +569,7 @@ framework_docs_SDK_REL_ID:=1 framework_docs_LOCAL_DROIDDOC_OPTIONS += \ -hdf sdk.version $(framework_docs_SDK_VERSION) \ -hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \ - -hdf sdk.preview 0 \ + -hdf sdk.preview 0 # ==== the api stubs and current.xml =========================== include $(CLEAR_VARS) @@ -755,6 +602,32 @@ $(full_target): $(framework_built) $(gen) $(INTERNAL_PLATFORM_API_FILE): $(full_target) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) +# ==== the private api stubs =================================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) + +LOCAL_MODULE := private-api-stubs + +LOCAL_DROIDDOC_OPTIONS:=\ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_private_stubs_current_intermediates/src \ + -showAnnotation android.annotation.PrivateApi \ + -nodocs + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk + +LOCAL_UNINSTALLABLE_MODULE := true + +include $(BUILD_DROIDDOC) + # ==== check javadoc comments but don't generate docs ======== include $(CLEAR_VARS) @@ -808,8 +681,6 @@ LOCAL_DROIDDOC_OPTIONS:=\ -todo $(OUT_DOCS)/$(LOCAL_MODULE)-docs-todo.html \ -sdkvalues $(OUT_DOCS) \ -hdf android.whichdoc offline -# $(web_docs_sample_code_flags) - LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk @@ -836,7 +707,7 @@ LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) -LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl /intl/ +LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl / LOCAL_MODULE := online-sdk @@ -845,7 +716,8 @@ LOCAL_DROIDDOC_OPTIONS:= \ -toroot / \ -hdf android.whichdoc online \ $(sample_groups) \ - $(web_docs_sample_code_flags) + -hdf android.hasSamples true \ + -samplesdir $(samples_dir) LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk @@ -863,7 +735,7 @@ LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) # specify a second html input dir and an output path relative to OUT_DIR) -LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl / +LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl/intl / LOCAL_MODULE := ds @@ -873,7 +745,6 @@ LOCAL_DROIDDOC_OPTIONS:= \ -toroot / \ -hdf android.whichdoc online \ -hdf devsite true -# $(web_docs_sample_code_flags) LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 2666b4172a08fde0e70d94cc3ef3df0700b50ac7..db3d8bbc9cfc7e24161df274650d81b1e4c80c89 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -187,6 +187,12 @@ public final class Bmgr { } private void doWipe() { + String transport = nextArg(); + if (transport == null) { + showUsage(); + return; + } + String pkg = nextArg(); if (pkg == null) { showUsage(); @@ -194,8 +200,8 @@ public final class Bmgr { } try { - mBmgr.clearBackupData(pkg); - System.out.println("Wiped backup data for " + pkg); + mBmgr.clearBackupData(transport, pkg); + System.out.println("Wiped backup data for " + pkg + " on " + transport); } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); @@ -446,7 +452,7 @@ public final class Bmgr { System.err.println(" bmgr restore TOKEN PACKAGE..."); System.err.println(" bmgr restore PACKAGE"); System.err.println(" bmgr run"); - System.err.println(" bmgr wipe PACKAGE"); + System.err.println(" bmgr wipe TRANSPORT PACKAGE"); System.err.println(""); System.err.println("The 'backup' command schedules a backup pass for the named package."); System.err.println("Note that the backup pass will effectively be a no-op if the package"); @@ -462,8 +468,8 @@ public final class Bmgr { System.err.println(""); System.err.println("The 'list transports' command reports the names of the backup transports"); System.err.println("currently available on the device. These names can be passed as arguments"); - System.err.println("to the 'transport' command. The currently selected transport is indicated"); - System.err.println("with a '*' character."); + System.err.println("to the 'transport' and 'wipe' commands. The currently selected transport"); + System.err.println("is indicated with a '*' character."); System.err.println(""); System.err.println("The 'list sets' command reports the token and name of each restore set"); System.err.println("available to the device via the current transport."); @@ -491,7 +497,8 @@ public final class Bmgr { System.err.println("data changes."); System.err.println(""); System.err.println("The 'wipe' command causes all backed-up data for the given package to be"); - System.err.println("erased from the current transport's storage. The next backup operation"); + System.err.println("erased from the given transport's storage. The next backup operation"); System.err.println("that the given application performs will rewrite its entire data set."); + System.err.println("Transport names to use here are those reported by 'list transports'."); } } diff --git a/core/java/android/annotation/PrivateApi.java b/core/java/android/annotation/PrivateApi.java new file mode 100644 index 0000000000000000000000000000000000000000..985eafec5d3371d807ddc6072759ecd1850e2483 --- /dev/null +++ b/core/java/android/annotation/PrivateApi.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates an API is exposed for use by bundled applications. + *

+ * These APIs are not guaranteed to remain consistent release-to-release, + * and are not for use by apps linking against the SDK. + * @hide + */ +@Retention(RetentionPolicy.SOURCE) +public @interface PrivateApi { +} diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b103e712600c3cdb34b174a1bb9d086fe751771f..9a3478efc166062e1fb0240a5e69b99655b8e6b3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -157,7 +157,7 @@ public final class ActivityThread { private static final int LOG_ON_PAUSE_CALLED = 30021; private static final int LOG_ON_RESUME_CALLED = 30022; - static ContextImpl mSystemContext = null; + private ContextImpl mSystemContext; static IPackageManager sPackageManager; @@ -1700,7 +1700,7 @@ public final class ActivityThread { ? mBoundApplication.processName : null) + ")"); packageInfo = - new LoadedApk(this, aInfo, compatInfo, this, baseLoader, + new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0); if (includeCode) { @@ -1753,26 +1753,15 @@ public final class ActivityThread { public ContextImpl getSystemContext() { synchronized (this) { if (mSystemContext == null) { - ContextImpl context = - ContextImpl.createSystemContext(this); - LoadedApk info = new LoadedApk(this, "android", context, null, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO); - context.init(info, null, this); - context.getResources().updateConfiguration(mResourcesManager.getConfiguration(), - mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); - mSystemContext = context; - //Slog.i(TAG, "Created system resources " + context.getResources() - // + ": " + context.getResources().getConfiguration()); + mSystemContext = ContextImpl.createSystemContext(this); } + return mSystemContext; } - return mSystemContext; } public void installSystemApplicationInfo(ApplicationInfo info) { synchronized (this) { - ContextImpl context = getSystemContext(); - context.init(new LoadedApk(this, "android", context, info, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this); + getSystemContext().installSystemApplicationInfo(info); // give ourselves a default profiler mProfiler = new Profiler(); @@ -2258,8 +2247,7 @@ public final class ActivityThread { private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { - ContextImpl appContext = new ContextImpl(); - appContext.init(r.packageInfo, r.token, this); + ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token); appContext.setOuterContext(activity); // For debugging purposes, if the activity's package name contains the value of @@ -2544,8 +2532,7 @@ public final class ActivityThread { agent = (BackupAgent) cl.loadClass(classname).newInstance(); // set up the agent's context - ContextImpl context = new ContextImpl(); - context.init(packageInfo, null, this); + ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(agent); agent.attach(context); @@ -2617,11 +2604,10 @@ public final class ActivityThread { try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); - ContextImpl context = new ContextImpl(); - context.init(packageInfo, null, this); + ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); - context.setOuterContext(service); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); service.onCreate(); @@ -4217,8 +4203,7 @@ public final class ActivityThread { } updateDefaultDensity(); - final ContextImpl appContext = new ContextImpl(); - appContext.init(data.info, null, this); + final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); if (!Process.isIsolated()) { final File cacheDir = appContext.getCacheDir(); @@ -4334,8 +4319,7 @@ public final class ActivityThread { instrApp.nativeLibraryDir = ii.nativeLibraryDir; LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, appContext.getClassLoader(), false, true); - ContextImpl instrContext = new ContextImpl(); - instrContext.init(pi, null, this); + ContextImpl instrContext = ContextImpl.createAppContext(this, pi); try { java.lang.ClassLoader cl = instrContext.getClassLoader(); @@ -4950,8 +4934,8 @@ public final class ActivityThread { UserHandle.myUserId()); try { mInstrumentation = new Instrumentation(); - ContextImpl context = new ContextImpl(); - context.init(getSystemContext().mPackageInfo, null, this); + ContextImpl context = ContextImpl.createAppContext( + this, getSystemContext().mPackageInfo); Application app = Instrumentation.newApplication(Application.class, context); mAllApplications.add(app); mInitialApplication = app; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index aece46298364d7cd7713046c56d8d2118f4f60ec..079cf7a210c9104efe0ec0f389cc3b0fd9706529 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -36,7 +36,7 @@ import android.os.RemoteException; * API for interacting with "application operation" tracking. * *

This API is not generally intended for third party application developers; most - * features are only available to system applicatins. Obtain an instance of it through + * features are only available to system applications. Obtain an instance of it through * {@link Context#getSystemService(String) Context.getSystemService} with * {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.

*/ @@ -878,7 +878,7 @@ public class AppOpsManager { } /** - * Like {@link #checkOp but instead of throwing a {@link SecurityException} it + * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. */ public int checkOpNoThrow(String op, int uid, String packageName) { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 8d127c6e381a8501f889a53f81f0864fbe20a09b..df50989c290e39c471bf5bd303eb2fb3783c4591 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -183,22 +183,31 @@ class ContextImpl extends Context { */ private static ArrayMap> sSharedPrefs; - /*package*/ LoadedApk mPackageInfo; - private String mBasePackageName; - private String mOpPackageName; - private Resources mResources; - /*package*/ ActivityThread mMainThread; + final ActivityThread mMainThread; + final LoadedApk mPackageInfo; + + private final IBinder mActivityToken; + + private final UserHandle mUser; + + private final ApplicationContentResolver mContentResolver; + + private final String mBasePackageName; + private final String mOpPackageName; + + private final ResourcesManager mResourcesManager; + private final Resources mResources; + private final Display mDisplay; // may be null if default display + private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments(); + private final Configuration mOverrideConfiguration; + + private final boolean mRestricted; + private Context mOuterContext; - private IBinder mActivityToken = null; - private ApplicationContentResolver mContentResolver; private int mThemeResource = 0; private Resources.Theme mTheme = null; private PackageManager mPackageManager; - private Display mDisplay; // may be null if default display private Context mReceiverRestrictedContext = null; - private boolean mRestricted; - private UserHandle mUser; - private ResourcesManager mResourcesManager; private final Object mSync = new Object(); @@ -220,8 +229,6 @@ class ContextImpl extends Context { private static final String[] EMPTY_FILE_LIST = {}; - final private DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments(); - /** * Override this class when the system service constructor needs a * ContextImpl. Else, use StaticServiceFetcher below. @@ -356,10 +363,11 @@ class ContextImpl extends Context { ctx.mMainThread.getHandler()); }}); - registerService(CONNECTIVITY_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { + registerService(CONNECTIVITY_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE); - return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b)); + return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b), + ctx.getPackageName()); }}); registerService(COUNTRY_DETECTOR, new StaticServiceFetcher() { @@ -1878,20 +1886,17 @@ class ContextImpl extends Context { @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { + final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; if (packageName.equals("system") || packageName.equals("android")) { - final ContextImpl context = new ContextImpl(mMainThread.getSystemContext()); - context.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; - context.init(mPackageInfo, null, mMainThread, mResources, mBasePackageName, user); - return context; + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, + user, restricted, mDisplay, mOverrideConfiguration); } - LoadedApk pi = - mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags, - user.getIdentifier()); + LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), + flags, user.getIdentifier()); if (pi != null) { - ContextImpl c = new ContextImpl(); - c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; - c.init(pi, null, mMainThread, mResources, mBasePackageName, user); + ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, + user, restricted, mDisplay, mOverrideConfiguration); if (c.mResources != null) { return c; } @@ -1899,7 +1904,7 @@ class ContextImpl extends Context { // Should be a better exception. throw new PackageManager.NameNotFoundException( - "Application package " + packageName + " not found"); + "Application package " + packageName + " not found"); } @Override @@ -1908,12 +1913,8 @@ class ContextImpl extends Context { throw new IllegalArgumentException("overrideConfiguration must not be null"); } - ContextImpl c = new ContextImpl(); - c.init(mPackageInfo, null, mMainThread); - c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), - mPackageInfo.getOverlayDirs(), getDisplayId(), overrideConfiguration, - mResources.getCompatibilityInfo(), mActivityToken); - return c; + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, + mUser, mRestricted, mDisplay, overrideConfiguration); } @Override @@ -1922,15 +1923,8 @@ class ContextImpl extends Context { throw new IllegalArgumentException("display must not be null"); } - int displayId = display.getDisplayId(); - - ContextImpl context = new ContextImpl(); - context.init(mPackageInfo, null, mMainThread); - context.mDisplay = display; - DisplayAdjustments daj = getDisplayAdjustments(displayId); - context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), - mPackageInfo.getOverlayDirs(), displayId, null, daj.getCompatibilityInfo(), null); - return context; + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, + mUser, mRestricted, display, mOverrideConfiguration); } private int getDisplayId() { @@ -1972,43 +1966,76 @@ class ContextImpl extends Context { } static ContextImpl createSystemContext(ActivityThread mainThread) { - final ContextImpl context = new ContextImpl(); - context.init(Resources.getSystem(), mainThread, Process.myUserHandle()); + LoadedApk packageInfo = new LoadedApk(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, + packageInfo, null, null, false, null, null); + context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), + context.mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); return context; } - ContextImpl() { - mOuterContext = this; + static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { + if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + return new ContextImpl(null, mainThread, + packageInfo, null, null, false, null, null); } - /** - * Create a new ApplicationContext from an existing one. The new one - * works and operates the same as the one it is copying. - * - * @param context Existing application context. - */ - public ContextImpl(ContextImpl context) { - mPackageInfo = context.mPackageInfo; - mBasePackageName = context.mBasePackageName; - mOpPackageName = context.mOpPackageName; - mResources = context.mResources; - mMainThread = context.mMainThread; - mContentResolver = context.mContentResolver; - mUser = context.mUser; - mDisplay = context.mDisplay; - mOuterContext = this; - mDisplayAdjustments.setCompatibilityInfo(mPackageInfo.getCompatibilityInfo()); + static ContextImpl createActivityContext(ActivityThread mainThread, + LoadedApk packageInfo, IBinder activityToken) { + if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + if (activityToken == null) throw new IllegalArgumentException("activityInfo"); + return new ContextImpl(null, mainThread, + packageInfo, activityToken, null, false, null, null); } - final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) { - init(packageInfo, activityToken, mainThread, null, null, Process.myUserHandle()); - } + private ContextImpl(ContextImpl container, ActivityThread mainThread, + LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, + Display display, Configuration overrideConfiguration) { + mOuterContext = this; + + mMainThread = mainThread; + mActivityToken = activityToken; + mRestricted = restricted; + + if (user == null) { + user = Process.myUserHandle(); + } + mUser = user; - final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread, - Resources container, String basePackageName, UserHandle user) { mPackageInfo = packageInfo; - if (basePackageName != null) { - mBasePackageName = mOpPackageName = basePackageName; + mContentResolver = new ApplicationContentResolver(this, mainThread, user); + mResourcesManager = ResourcesManager.getInstance(); + mDisplay = display; + mOverrideConfiguration = overrideConfiguration; + + final int displayId = getDisplayId(); + CompatibilityInfo compatInfo = null; + if (container != null) { + compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo(); + } + if (compatInfo == null && displayId == Display.DEFAULT_DISPLAY) { + compatInfo = packageInfo.getCompatibilityInfo(); + } + mDisplayAdjustments.setCompatibilityInfo(compatInfo); + mDisplayAdjustments.setActivityToken(activityToken); + + Resources resources = packageInfo.getResources(mainThread); + if (resources != null) { + if (activityToken != null + || displayId != Display.DEFAULT_DISPLAY + || overrideConfiguration != null + || (compatInfo != null && compatInfo.applicationScale + != resources.getCompatibilityInfo().applicationScale)) { + resources = mResourcesManager.getTopLevelResources( + packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId, + overrideConfiguration, compatInfo, activityToken); + } + } + mResources = resources; + + if (container != null) { + mBasePackageName = container.mBasePackageName; + mOpPackageName = container.mOpPackageName; } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); @@ -2022,45 +2049,10 @@ class ContextImpl extends Context { mOpPackageName = mBasePackageName; } } - mResources = mPackageInfo.getResources(mainThread); - mResourcesManager = ResourcesManager.getInstance(); - - CompatibilityInfo compatInfo = - container == null ? null : container.getCompatibilityInfo(); - if (mResources != null && - ((compatInfo != null && compatInfo.applicationScale != - mResources.getCompatibilityInfo().applicationScale) - || activityToken != null)) { - if (DEBUG) { - Log.d(TAG, "loaded context has different scaling. Using container's" + - " compatiblity info:" + container.getDisplayMetrics()); - } - if (compatInfo == null) { - compatInfo = packageInfo.getCompatibilityInfo(); - } - mDisplayAdjustments.setCompatibilityInfo(compatInfo); - mDisplayAdjustments.setActivityToken(activityToken); - mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), - mPackageInfo.getOverlayDirs(), Display.DEFAULT_DISPLAY, null, compatInfo, - activityToken); - } else { - mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo()); - mDisplayAdjustments.setActivityToken(activityToken); - } - mMainThread = mainThread; - mActivityToken = activityToken; - mContentResolver = new ApplicationContentResolver(this, mainThread, user); - mUser = user; } - final void init(Resources resources, ActivityThread mainThread, UserHandle user) { - mPackageInfo = null; - mBasePackageName = null; - mOpPackageName = null; - mResources = resources; - mMainThread = mainThread; - mContentResolver = new ApplicationContentResolver(this, mainThread, user); - mUser = user; + void installSystemApplicationInfo(ApplicationInfo info) { + mPackageInfo.installSystemApplicationInfo(info); } final void scheduleFinalCleanup(String who, String what) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0115d1bcc9543615bd7c6f7076cd74f54ebe86a3..d409352149221c051994e0246b91ed18ef169fae 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -72,7 +72,7 @@ public final class LoadedApk { private static final String TAG = "LoadedApk"; private final ActivityThread mActivityThread; - private final ApplicationInfo mApplicationInfo; + private ApplicationInfo mApplicationInfo; final String mPackageName; private final String mAppDir; private final String mResDir; @@ -111,8 +111,7 @@ public final class LoadedApk { * so MUST NOT call back out to the activity manager. */ public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo, - CompatibilityInfo compatInfo, - ActivityThread mainThread, ClassLoader baseLoader, + CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { mActivityThread = activityThread; mApplicationInfo = aInfo; @@ -134,31 +133,17 @@ public final class LoadedApk { mSecurityViolation = securityViolation; mIncludeCode = includeCode; mDisplayAdjustments.setCompatibilityInfo(compatInfo); - - if (mAppDir == null) { - if (ActivityThread.mSystemContext == null) { - ActivityThread.mSystemContext = - ContextImpl.createSystemContext(mainThread); - ResourcesManager resourcesManager = ResourcesManager.getInstance(); - ActivityThread.mSystemContext.getResources().updateConfiguration( - resourcesManager.getConfiguration(), - resourcesManager.getDisplayMetricsLocked( - Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo); - //Slog.i(TAG, "Created system resources " - // + mSystemContext.getResources() + ": " - // + mSystemContext.getResources().getConfiguration()); - } - mClassLoader = ActivityThread.mSystemContext.getClassLoader(); - mResources = ActivityThread.mSystemContext.getResources(); - } } - public LoadedApk(ActivityThread activityThread, String name, - Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) { + /** + * Create information about the system package. + * Must call {@link #installSystemApplicationInfo} later. + */ + LoadedApk(ActivityThread activityThread) { mActivityThread = activityThread; - mApplicationInfo = info != null ? info : new ApplicationInfo(); - mApplicationInfo.packageName = name; - mPackageName = name; + mApplicationInfo = new ApplicationInfo(); + mApplicationInfo.packageName = "android"; + mPackageName = "android"; mAppDir = null; mResDir = null; mOverlayDirs = null; @@ -169,9 +154,16 @@ public final class LoadedApk { mBaseClassLoader = null; mSecurityViolation = false; mIncludeCode = true; - mClassLoader = systemContext.getClassLoader(); - mResources = systemContext.getResources(); - mDisplayAdjustments.setCompatibilityInfo(compatInfo); + mClassLoader = ClassLoader.getSystemClassLoader(); + mResources = Resources.getSystem(); + } + + /** + * Sets application info about the system package. + */ + void installSystemApplicationInfo(ApplicationInfo info) { + assert info.packageName.equals("android"); + mApplicationInfo = info; } public String getPackageName() { @@ -513,8 +505,7 @@ public final class LoadedApk { try { java.lang.ClassLoader cl = getClassLoader(); - ContextImpl appContext = new ContextImpl(); - appContext.init(this, null, mActivityThread); + ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c63e586a7127c35984facc1e4afe4c729a234427..cce6fc42da58c162b5ab6e2d0a5d99f3a1510050 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1778,7 +1778,7 @@ public class Notification implements Parcelable RemoteViews button = new RemoteViews(mContext.getPackageName(), tombstone ? R.layout.notification_action_tombstone : R.layout.notification_action); - button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); + button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0); button.setTextViewText(R.id.action0, action.title); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index bdd0adbbb43622968bec46d105e0eb6b284bea80..45467b8d9e18ec7dc1022d7350c1c97e1a5cdb31 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -87,23 +87,25 @@ public final class PendingIntent implements Parcelable { private final IIntentSender mTarget; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: this - * PendingIntent can only be used once. If set, after + * Flag indicating that this PendingIntent can be used only once. + * For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}.

If set, after * {@link #send()} is called on it, it will be automatically * canceled for you and any future attempt to send through it will fail. */ public static final int FLAG_ONE_SHOT = 1<<30; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: if the described PendingIntent does not already - * exist, then simply return null instead of creating it. + * Flag indicating that if the described PendingIntent already + * exists, then simply return null instead of creating it. + * For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}. */ public static final int FLAG_NO_CREATE = 1<<29; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: if the described PendingIntent already exists, - * the current one is canceled before generating a new one. You can use + * Flag indicating that if the described PendingIntent already exists, + * the current one should be canceled before generating a new one. + * For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}.

You can use * this to retrieve a new PendingIntent when you are only changing the * extra data in the Intent; by canceling the previous pending intent, * this ensures that only entities given the new data will be able to @@ -112,10 +114,10 @@ public final class PendingIntent implements Parcelable { */ public static final int FLAG_CANCEL_CURRENT = 1<<28; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: if the described PendingIntent already exists, - * then keep it but its replace its extra data with what is in this new - * Intent. This can be used if you are creating intents where only the + * Flag indicating that if the described PendingIntent already exists, + * then keep it but replace its extra data with what is in this new + * Intent. For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}.

This can be used if you are creating intents where only the * extras change, and don't care that any entities that received your * previous PendingIntent will be able to launch it with your new * extras even if they are not explicitly given to it. @@ -203,7 +205,7 @@ public final class PendingIntent implements Parcelable { *

For security reasons, the {@link android.content.Intent} * you supply here should almost always be an explicit intent, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass

+ * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

* * @param context The Context in which this PendingIntent should start * the activity. @@ -234,7 +236,7 @@ public final class PendingIntent implements Parcelable { *

For security reasons, the {@link android.content.Intent} * you supply here should almost always be an explicit intent, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass

+ * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

* * @param context The Context in which this PendingIntent should start * the activity. @@ -326,7 +328,7 @@ public final class PendingIntent implements Parcelable { *

For security reasons, the {@link android.content.Intent} objects * you supply here should almost always be explicit intents, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass

+ * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

* * @param context The Context in which this PendingIntent should start * the activity. @@ -376,7 +378,7 @@ public final class PendingIntent implements Parcelable { *

For security reasons, the {@link android.content.Intent} objects * you supply here should almost always be explicit intents, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass

+ * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

* * @param context The Context in which this PendingIntent should start * the activity. @@ -446,7 +448,7 @@ public final class PendingIntent implements Parcelable { *

For security reasons, the {@link android.content.Intent} * you supply here should almost always be an explicit intent, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass

+ * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

* * @param context The Context in which this PendingIntent should perform * the broadcast. @@ -500,7 +502,7 @@ public final class PendingIntent implements Parcelable { *

For security reasons, the {@link android.content.Intent} * you supply here should almost always be an explicit intent, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass

+ * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

* * @param context The Context in which this PendingIntent should start * the service. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index ced72f80a0295a17d2d7bd780d7feb53ea845dc1..f291e8298bc1f844510da9189c46719ddb37eb42 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -45,9 +45,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.DisplayMetrics; import android.util.Log; -import android.view.WindowManager; import android.view.WindowManagerGlobal; import java.io.BufferedInputStream; @@ -294,9 +292,8 @@ public class WallpaperManager { try { BitmapFactory.Options options = new BitmapFactory.Options(); - Bitmap bm = BitmapFactory.decodeFileDescriptor( + return BitmapFactory.decodeFileDescriptor( fd.getFileDescriptor(), null, options); - return generateBitmap(context, bm, width, height); } catch (OutOfMemoryError e) { Log.w(TAG, "Can't decode file", e); } finally { @@ -323,8 +320,7 @@ public class WallpaperManager { try { BitmapFactory.Options options = new BitmapFactory.Options(); - Bitmap bm = BitmapFactory.decodeStream(is, null, options); - return generateBitmap(context, bm, width, height); + return BitmapFactory.decodeStream(is, null, options); } catch (OutOfMemoryError e) { Log.w(TAG, "Can't decode stream", e); } finally { @@ -1029,62 +1025,4 @@ public class WallpaperManager { public void clear() throws IOException { setResource(com.android.internal.R.drawable.default_wallpaper); } - - static Bitmap generateBitmap(Context context, Bitmap bm, int width, int height) { - if (bm == null) { - return null; - } - - WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); - DisplayMetrics metrics = new DisplayMetrics(); - wm.getDefaultDisplay().getMetrics(metrics); - bm.setDensity(metrics.noncompatDensityDpi); - - if (width <= 0 || height <= 0 - || (bm.getWidth() == width && bm.getHeight() == height)) { - return bm; - } - - // This is the final bitmap we want to return. - try { - Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - newbm.setDensity(metrics.noncompatDensityDpi); - - Canvas c = new Canvas(newbm); - Rect targetRect = new Rect(); - targetRect.right = bm.getWidth(); - targetRect.bottom = bm.getHeight(); - - int deltaw = width - targetRect.right; - int deltah = height - targetRect.bottom; - - if (deltaw > 0 || deltah > 0) { - // We need to scale up so it covers the entire area. - float scale; - if (deltaw > deltah) { - scale = width / (float)targetRect.right; - } else { - scale = height / (float)targetRect.bottom; - } - targetRect.right = (int)(targetRect.right*scale); - targetRect.bottom = (int)(targetRect.bottom*scale); - deltaw = width - targetRect.right; - deltah = height - targetRect.bottom; - } - - targetRect.offset(deltaw/2, deltah/2); - - Paint paint = new Paint(); - paint.setFilterBitmap(true); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); - c.drawBitmap(bm, null, targetRect, paint); - - bm.recycle(); - c.setBitmap(null); - return newbm; - } catch (OutOfMemoryError e) { - Log.w(TAG, "Can't generate default bitmap", e); - return bm; - } - } } diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index bb4f5f160bbeb42c820051ec0e432f71aef2853f..12ee3b624f63f29a28512d3d2d8772c42680234a 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -43,14 +43,14 @@ interface IBackupManager { void dataChanged(String packageName); /** - * Erase all backed-up data for the given package from the storage + * Erase all backed-up data for the given package from the given storage * destination. * * Any application can invoke this method for its own package, but * only callers who hold the android.permission.BACKUP permission * may invoke it for arbitrary packages. */ - void clearBackupData(String packageName); + void clearBackupData(String transportName, String packageName); /** * Notifies the Backup Manager Service that an agent has become available. This diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e2bc80aad66d0afea95856c304062827efbcf3e3..75b007c75ce38f284a2b27ac4b04821a3ff9a502 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -20,21 +20,23 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.Pair; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; -import java.util.LinkedList; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Random; @@ -213,6 +215,22 @@ public final class BluetoothAdapter { public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; + /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} * intents to request the current scan mode. Possible values are: @@ -251,7 +269,6 @@ public final class BluetoothAdapter { */ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; - /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -350,9 +367,27 @@ public final class BluetoothAdapter { /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; + /** States for Bluetooth LE advertising */ + /** @hide */ + public static final int STATE_ADVERTISE_STARTING = 0; + /** @hide */ + public static final int STATE_ADVERTISE_STARTED = 1; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPING = 2; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPED = 3; + /** + * Force stopping advertising without callback in case the advertising app dies. + * @hide + */ + public static final int STATE_ADVERTISE_FORCE_STOPPING = 4; + /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + /** @hide */ + public static final int ADVERTISE_CALLBACK_SUCCESS = 0; + private static final int ADDRESS_LENGTH = 17; /** @@ -365,6 +400,10 @@ public final class BluetoothAdapter { private IBluetooth mService; private final Map mLeScanClients; + private BluetoothAdvScanData mBluetoothAdvScanData = null; + private GattCallbackWrapper mAdvertisingGattCallback; + private final Handler mHandler; // Handler to post the advertise callback to run on main thread. + private final Object mLock = new Object(); /** * Get a handle to the default local Bluetooth adapter. @@ -400,6 +439,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap(); + mHandler = new Handler(Looper.getMainLooper()); } /** @@ -437,6 +477,129 @@ public final class BluetoothAdapter { address[0], address[1], address[2], address[3], address[4], address[5])); } + /** + * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * Data will be reset when bluetooth service is turned off. + * @hide + */ + public BluetoothAdvScanData getAdvScanData() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + Log.e(TAG, "failed to start, iGatt null"); + return null; + } + if (mBluetoothAdvScanData == null) { + mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD); + } + return mBluetoothAdvScanData; + } catch (RemoteException e) { + Log.e(TAG, "failed to get advScanData, error: " + e); + return null; + } + } + + /** + * Interface for BLE advertising callback. + * + * @hide + */ + public interface AdvertiseCallback { + /** + * Callback when advertise starts. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStart(int status); + /** + * Callback when advertise stops. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStop(int status); + } + + /** + * Start BLE advertising using current {@link BluetoothAdvScanData}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @param callback - {@link AdvertiseCallback} + * @return true if BLE advertising succeeds, false otherwise. + * @hide + */ + public boolean startAdvertising(final AdvertiseCallback callback) { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported. + return false; + } + // Restart/reset advertising packets if advertising is in progress. + if (isAdvertising()) { + // Invalid advertising callback. + if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) { + Log.e(TAG, "failed to restart advertising, invalid callback"); + return false; + } + iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle); + // Run the callback from main thread. + mHandler.post(new Runnable() { + @Override + public void run() { + // callback with status success. + callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS); + } + }); + return true; + } + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = + new GattCallbackWrapper(this, null, null, callback); + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (!wrapper.advertiseStarted()) { + return false; + } + synchronized (mLock) { + mAdvertisingGattCallback = wrapper; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Stop BLE advertising. + * + * @param callback - {@link AdvertiseCallback} + * @return true if BLE advertising stops, false otherwise. + * @hide + */ + public boolean stopAdvertising(AdvertiseCallback callback) { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + if (mAdvertisingGattCallback == null) { + // no callback. + return false; + } + // Make sure same callback is used for start and stop advertising. + if (callback != mAdvertisingGattCallback.mAdvertiseCallback) { + Log.e(TAG, "must use the same callback for star/stop advertising"); + return false; + } + mAdvertisingGattCallback.stopAdvertising(); + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** * Return true if Bluetooth is currently enabled and ready for use. *

Equivalent to: @@ -848,6 +1011,23 @@ public final class BluetoothAdapter { return false; } + /** + * Returns whether BLE is currently advertising. + *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @hide + */ + public boolean isAdvertising() { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return iGatt.isAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. @@ -1272,6 +1452,8 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; + // Reset bluetooth adv scan data when Gatt service is down. + mBluetoothAdvScanData = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1547,7 +1729,9 @@ public final class BluetoothAdapter { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + private final AdvertiseCallback mAdvertiseCallback; private final LeScanCallback mLeScanCb; + // mLeHandle 0: not registered // -1: scan stopped // >0: registered and scan started @@ -1561,16 +1745,34 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; + mAdvertiseCallback = null; + } + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, + UUID[] uuid, AdvertiseCallback callback) { + mBluetoothAdapter = new WeakReference(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + mAdvertiseCallback = callback; } public boolean scanStarted() { + return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT); + } + + public boolean advertiseStarted() { + // Wait for registeration callback. + return waitForRegisteration(1); + } + + private boolean waitForRegisteration(int maxWaitCount) { boolean started = false; synchronized(this) { if (mLeHandle == -1) return false; - int count = 0; // wait for callback registration and LE scan to start - while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) { + while (mLeHandle == 0 && count < maxWaitCount) { try { wait(LE_CALLBACK_REG_TIMEOUT); } catch (InterruptedException e) { @@ -1583,6 +1785,27 @@ public final class BluetoothAdapter { return started; } + public void stopAdvertising() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising" + e); + } + } else { + Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); + } + notifyAll(); + } + } + public void stopLeScan() { synchronized(this) { if (mLeHandle <= 0) { @@ -1624,14 +1847,18 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); + if (mAdvertiseCallback != null) { + iGatt.startAdvertising(mLeHandle); } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } } } else { Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); @@ -1642,7 +1869,7 @@ public final class BluetoothAdapter { mLeHandle = -1; } if (mLeHandle == -1) { - // registration succeeded but start scan failed + // registration succeeded but start scan or advertise failed if (iGatt != null) { try { iGatt.unregisterClient(mLeHandle); @@ -1670,7 +1897,7 @@ public final class BluetoothAdapter { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); // Check null in case the scan has been stopped synchronized(this) { @@ -1759,9 +1986,33 @@ public final class BluetoothAdapter { // no op } - public void onListen(int status) { - // no op + public void onAdvertiseStateChange(int advertiseState, int status) { + Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); + if (advertiseState == STATE_ADVERTISE_STARTED) { + mAdvertiseCallback.onAdvertiseStart(status); + } else { + synchronized (this) { + if (status == ADVERTISE_CALLBACK_SUCCESS) { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = + adapter.getBluetoothManager().getBluetoothGatt(); + Log.d(TAG, "unregistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + // Reset advertise app handle. + mLeHandle = -1; + adapter.mAdvertisingGattCallback = null; + } catch (RemoteException e) { + Log.e(TAG, "Failed to unregister client" + e); + } + } else { + Log.e(TAG, "cannot unregister client, BluetoothAdapter is null"); + } + } + } + mAdvertiseCallback.onAdvertiseStop(status); + } } } - } diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java new file mode 100644 index 0000000000000000000000000000000000000000..df2c25629a3bef72d128e114ad1eb0b12800dff0 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAdvScanData.java @@ -0,0 +1,152 @@ +/* + * 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 android.bluetooth; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + + +/** + * This class provides the public APIs to set advertising and scan response data when BLE device + * operates in peripheral mode.
+ * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * @hide + */ +public final class BluetoothAdvScanData { + + /** + * Available data types of {@link BluetoothAdvScanData}. + */ + public static final int AD = 0; // Advertising Data + public static final int SCAN_RESPONSE = 1; // Scan Response + public static final int EIR = 2; // Extended Inquiry Response + + private static final String TAG = "BluetoothAdvScanData"; + + /** + * Data type of BluetoothAdvScanData. + */ + private final int mDataType; + /** + * Bluetooth Gatt Service. + */ + private IBluetoothGatt mBluetoothGatt; + + /** + * @param mBluetoothGatt + * @param dataType + */ + public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) { + this.mBluetoothGatt = mBluetoothGatt; + this.mDataType = dataType; + } + + /** + * @return advertising data type. + */ + public int getDataType() { + return mDataType; + } + + /** + * Set manufactureCode and manufactureData. + * Returns true if manufacturer data is set, false if there is no enough room to set + * manufacturer data or the data is already set. + * @param manufacturerCode - unique identifier for the manufacturer + * @param manufacturerData - data associated with the specific manufacturer. + */ + public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) { + if (mDataType != AD) return false; + try { + return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); + } catch (RemoteException e) { + Log.e(TAG, "Unable to set manufacturer id and data.", e); + return false; + } + } + + /** + * Set service data. Note the service data can only be set when the data type is {@code AD}; + * @param serviceData + */ + public boolean setServiceData(byte[] serviceData) { + + if (mDataType != AD) return false; + if (serviceData == null) return false; + try { + return mBluetoothGatt.setAdvServiceData(serviceData); + } catch (RemoteException e) { + Log.e(TAG, "Unable to set service data.", e); + return false; + } + } + + /** + * Returns an immutable list of service uuids that will be advertised. + */ + public List getServiceUuids() { + try { + return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); + } catch (RemoteException e) { + Log.e(TAG, "Unable to get service uuids.", e); + return null; + } + } + + /** + * Returns manufacturer data. + */ + public byte[] getManufacturerData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvManufacturerData(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to get manufacturer data.", e); + return null; + } + } + + /** + * Returns service data. + */ + public byte[] getServiceData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvServiceData(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to get service data.", e); + return null; + } + } + + /** + * Remove manufacturer data based on given manufacturer code. + * @param manufacturerCode + */ + public void removeManufacturerCodeAndData(int manufacturerCode) { + if (mBluetoothGatt != null) { + try { + mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); + } catch (RemoteException e) { + Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e); + } + } + } +} diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index a2bb78c4c158f6790f381528ca4950536a778262..ae6ad3b4f269fed637e17fff2cfbe06197e720a7 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -555,11 +555,12 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Listen command status callback + * Advertise state change callback * @hide */ - public void onListen(int status) { - if (DBG) Log.d(TAG, "onListen() - status=" + status); + public void onAdvertiseStateChange(int state, int status) { + if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + + state + " status=" + status); } }; @@ -693,71 +694,6 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } - /** - * Starts or stops sending of advertisement packages to listen for connection - * requests from a central devices. - * - *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param start Start or stop advertising - */ - /*package*/ void listen(boolean start) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); - } - if (DBG) Log.d(TAG, "listen() - start: " + start); - if (mService == null || mClientIf == 0) return; - - try { - mService.clientListen(mClientIf, start); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Sets the advertising data contained in the adv. response packet. - * - *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param advData true to set adv. data, false to set scan response - * @param includeName Inlucde the name in the adv. response - * @param includeTxPower Include TX power value - * @param minInterval Minimum desired scan interval (optional) - * @param maxInterval Maximum desired scan interval (optional) - * @param appearance The appearance flags for the device (optional) - * @param manufacturerData Manufacturer specific data including company ID (optional) - */ - /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, - Integer minInterval, Integer maxInterval, - Integer appearance, Byte[] manufacturerData) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); - } - if (DBG) Log.d(TAG, "setAdvData()"); - if (mService == null || mClientIf == 0) return; - - byte[] data = new byte[0]; - if (manufacturerData != null) { - data = new byte[manufacturerData.length]; - for(int i = 0; i != manufacturerData.length; ++i) { - data[i] = manufacturerData[i]; - } - } - - try { - mService.setAdvData(mClientIf, !advData, - includeName, includeTxPower, - minInterval != null ? minInterval : 0, - maxInterval != null ? maxInterval : 0, - appearance != null ? appearance : 0, data); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 58ee54fdb1004525a7e8ac63b273707849c97c7b..153215cb525697e601b2eb5509c3f32729edb344 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -537,7 +537,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.beginServiceDeclaration(mServerIf, service.getType(), service.getInstanceId(), service.getHandles(), - new ParcelUuid(service.getUuid())); + new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); List includedServices = service.getIncludedServices(); for (BluetoothGattService includedService : includedServices) { diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java index 1e66369601057e5c75da559cf036e0c604e18d0b..52bc0f796cb59d089ee1c2289d2fb7c8ddb26ecb 100644 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -15,8 +15,6 @@ */ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -81,6 +79,11 @@ public class BluetoothGattService { */ protected List mIncludedServices; + /** + * Whether the service uuid should be advertised. + */ + private boolean mAdvertisePreferred; + /** * Create a new BluetoothGattService. *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -263,4 +266,20 @@ public class BluetoothGattService { } return null; } + + /** + * Returns whether the uuid of the service should be advertised. + * @hide + */ + public boolean isAdvertisePreferred() { + return mAdvertisePreferred; + } + + /** + * Set whether the service uuid should be advertised. + * @hide + */ + public void setAdvertisePreferred(boolean advertisePreferred) { + this.mAdvertisePreferred = advertisePreferred; + } } diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index abdf949ebcfd01ff3fbbf07ead153882dff26525..4b28516e15a2ef7c03a4c720d2ca14c622372707 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -73,6 +73,9 @@ public final class BluetoothUuid { public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = + ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -211,4 +214,17 @@ public final class BluetoothUuid { long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; return (int)value; } + + /** + * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * @param parcelUuid + * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + */ + public static boolean isShortUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index df393dbb2899b372e227cbc9407288be2eb55459..784cdcc5318cc306ce37824eed664476cecf1f6b 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -37,10 +37,15 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect); void clientDisconnect(in int clientIf, in String address); - void clientListen(in int clientIf, in boolean start); - void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName, - in boolean inclTxPower, in int minInterval, in int maxInterval, - in int appearance, in byte[] manufacturerData); + void startAdvertising(in int appIf); + void stopAdvertising(); + boolean setAdvServiceData(in byte[] serviceData); + byte[] getAdvServiceData(); + boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData); + byte[] getAdvManufacturerData(); + List getAdvServiceUuids(); + void removeAdvManufacturerCodeAndData(int manufacturerCode); + boolean isAdvertising(); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, @@ -75,7 +80,7 @@ interface IBluetoothGatt { void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, - in ParcelUuid srvcId); + in ParcelUuid srvcId, boolean advertisePreferred); void addIncludedService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId); void addCharacteristic(in int serverIf, in ParcelUuid charId, diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index 60c297b59798f1085952a643cb277a7f663d6042..7c69a066b46518ec98a491cdbda8e22c79e8838c 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,5 +63,5 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onListen(in int status); + oneway void onAdvertiseStateChange(in int advertiseState, in int status); } diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html index 200a21b8acff10db2c005f45673f5b2eb433f10f..d9ca4f13101f8189277b1733ac215213da4c106b 100644 --- a/core/java/android/bluetooth/package.html +++ b/core/java/android/bluetooth/package.html @@ -8,17 +8,19 @@ The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.

Bluetooth guide. For more information about Bluetooth Low Energy, see the -Bluetooth Low Energy guide.

+Bluetooth Low Energy (BLE) guide.

{@more}

The Bluetooth APIs let applications:

    -
  • Scan for other Bluetooth devices (including Bluetooth Low Energy - devices)
  • -
  • Query the local Bluetooth adapter for paired Bluetooth devices
  • -
  • Establish RFCOMM channels/sockets
  • -
  • Connect to specified sockets on other devices
  • -
  • Transfer data to and from other devices
  • +
  • Scan for other Bluetooth devices (including BLE devices).
  • +
  • Query the local Bluetooth adapter for paired Bluetooth devices.
  • +
  • Establish RFCOMM channels/sockets.
  • +
  • Connect to specified sockets on other devices.
  • +
  • Transfer data to and from other devices.
  • +
  • Communicate with BLE devices, such as proximity sensors, heart rate + monitors, fitness devices, and so on.
  • +
  • Act as a GATT client or a GATT server (BLE).

diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index 5cb6e77415e80ee7567fe012f6829e58bc73a981..be35f087a5e56e5d8bb31019cc03d2351fe75242 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -87,7 +87,7 @@ public class ClipDescription implements Parcelable { /** * Helper to compare two MIME types, where one may be a pattern. * @param concreteType A fully-specified MIME type. - * @param desiredType A desired MIME type that may be a pattern such as *\/*. + * @param desiredType A desired MIME type that may be a pattern such as */*. * @return Returns true if the two MIME types match. */ public static boolean compareMimeTypes(String concreteType, String desiredType) { diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 44831fc8f41fa69a33598fa0c81e40f18cfd2f4d..02c850bc47c4f06e8754fc2093c1e2aed65f3601 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -1328,7 +1328,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be - * a pattern, such as *\/* to retrieve all possible data types. + * a pattern, such as */* to retrieve all possible data types. * @return Returns {@code null} if there are no possible data streams for the * given mimeTypeFilter. Otherwise returns an array of all available * concrete MIME types. @@ -1366,7 +1366,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be - * a pattern, such as *\/*, if the caller does not have specific type + * a pattern, such as */*, if the caller does not have specific type * requirements; in this case the content provider will pick its best * type matching the pattern. * @param opts Additional options from the client. The definitions of @@ -1427,7 +1427,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be - * a pattern, such as *\/*, if the caller does not have specific type + * a pattern, such as */*, if the caller does not have specific type * requirements; in this case the content provider will pick its best * type matching the pattern. * @param opts Additional options from the client. The definitions of diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 4e6cc925ee5d5fbffc7ca0618a75241507eb3142..2bf4d7d3dd7ae2a391099a90368ec5f15f8e33ee 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -344,7 +344,7 @@ public abstract class ContentResolver { * @param url A Uri identifying content (either a list or specific type), * using the content:// scheme. * @param mimeTypeFilter The desired MIME type. This may be a pattern, - * such as *\/*, to query for all available MIME types that match the + * such as */*, to query for all available MIME types that match the * pattern. * @return Returns an array of MIME type strings for all available * data streams that match the given mimeTypeFilter. If there are none, @@ -815,7 +815,7 @@ public abstract class ContentResolver { * *

Note that if this function is called for read-only input (mode is "r") * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor} - * for you with a MIME type of "*\/*". This allows such callers to benefit + * for you with a MIME type of "*/*". This allows such callers to benefit * from any built-in data conversion that a provider implements. * * @param uri The desired URI to open. @@ -868,7 +868,7 @@ public abstract class ContentResolver { * *

Note that if this function is called for read-only input (mode is "r") * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor} - * for you with a MIME type of "*\/*". This allows such callers to benefit + * for you with a MIME type of "*/*". This allows such callers to benefit * from any built-in data conversion that a provider implements. * * @param uri The desired URI to open. @@ -993,7 +993,7 @@ public abstract class ContentResolver { * * @param uri The desired URI to open. * @param mimeType The desired MIME type of the returned data. This can - * be a pattern such as *\/*, which will allow the content provider to + * be a pattern such as */*, which will allow the content provider to * select a type, though there is no way for you to determine what type * it is returning. * @param opts Additional provider-dependent options. @@ -1026,7 +1026,7 @@ public abstract class ContentResolver { * * @param uri The desired URI to open. * @param mimeType The desired MIME type of the returned data. This can - * be a pattern such as *\/*, which will allow the content provider to + * be a pattern such as */*, which will allow the content provider to * select a type, though there is no way for you to determine what type * it is returning. * @param opts Additional provider-dependent options. @@ -1535,7 +1535,7 @@ public abstract class ContentResolver { * for a whole class of content. * @param notifyForDescendents If true changes to URIs beginning with uri * will also cause notifications to be sent. If false only changes to the exact URI - * specified by uri will cause notifications to be sent. If true, than any URI values + * specified by uri will cause notifications to be sent. If true, any URI values * at or below the specified URI will also trigger a match. * @param observer The object that receives callbacks when changes occur. * @see #unregisterContentObserver diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 2e2d4b81777a9e3c15b8b625a5a9f29c40f70e9e..a50b65051fd6bb1b6930aab05542713a11c227be 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -844,7 +844,7 @@ public class Intent implements Parcelable, Cloneable { * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be * set in the returned chooser intent, with its ClipData set appropriately: * either a direct reflection of {@link #getClipData()} if that is non-null, - * or a new ClipData build from {@link #getData()}. + * or a new ClipData built from {@link #getData()}. * * @param target The Intent that the user will be selecting an activity * to perform. @@ -4424,7 +4424,7 @@ public class Intent implements Parcelable, Cloneable { * Return the {@link ClipData} associated with this Intent. If there is * none, returns null. See {@link #setClipData} for more information. * - * @see #setClipData; + * @see #setClipData */ public ClipData getClipData() { return mClipData; @@ -5440,7 +5440,7 @@ public class Intent implements Parcelable, Cloneable { * directly used by Intent. Applications should generally rely on the * MIME type of the Intent itself, not what it may find in the ClipData. * A common practice is to construct a ClipData for use with an Intent - * with a MIME type of "*\/*". + * with a MIME type of "*/*". * * @param clip The new clip to set. May be null to clear the current clip. */ @@ -7162,8 +7162,8 @@ public class Intent implements Parcelable, Cloneable { * * @param type MIME data type to normalize * @return normalized MIME data type, or null if the input was null - * @see {@link #setType} - * @see {@link #setTypeAndNormalize} + * @see #setType + * @see #setTypeAndNormalize */ public static String normalizeMimeType(String type) { if (type == null) { diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 111062d630b14283638158e81d022c6219ec6e08..eae4a46d35e8f231887e05e13c7f7a2d6ca8a7a9 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -45,6 +45,7 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.locks.ReentrantLock; @@ -2150,10 +2151,20 @@ public class Camera { private static final String PIXEL_FORMAT_JPEG = "jpeg"; private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; - private HashMap mMap; + /** + * Order matters: Keys that are {@link #set(String, String) set} later + * will take precedence over keys that are set earlier (if the two keys + * conflict with each other). + * + *

One example is {@link #setPreviewFpsRange(int, int)} , since it + * conflicts with {@link #setPreviewFrameRate(int)} whichever key is set later + * is the one that will take precedence. + *

+ */ + private final LinkedHashMap mMap; private Parameters() { - mMap = new HashMap(64); + mMap = new LinkedHashMap(/*initialCapacity*/64); } /** @@ -2233,7 +2244,7 @@ public class Camera { return; } - mMap.put(key, value); + put(key, value); } /** @@ -2243,7 +2254,18 @@ public class Camera { * @param value the int value of the parameter */ public void set(String key, int value) { - mMap.put(key, Integer.toString(value)); + put(key, Integer.toString(value)); + } + + private void put(String key, String value) { + /* + * Remove the key if it already exists. + * + * This way setting a new value for an already existing key will always move + * that key to be ordered the latest in the map. + */ + mMap.remove(key); + mMap.put(key, value); } private void set(String key, List areas) { diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index ac9189d16577897f12e55a826263ba3ac4609283..5f2b5f057b40a248429f4cc7a4d12615cde34c37 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1056,8 +1056,8 @@ public abstract class SensorManager { * is mapped. * * @param outR - * the transformed rotation matrix. inR and outR can be the same - * array, but it is not recommended for performance reason. + * the transformed rotation matrix. inR and outR should not be the same + * array. * * @return true on success. false if the input * parameters are incorrect, for instance if X and Y define the same diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index d5208d97bd41d952ae0194b9445e75c42945e88d..093e0e9270ff64a2b1277722b98fc3c04f55c25c 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -297,16 +297,31 @@ public final class DisplayManager { } /** - * Initiates a fresh scan of availble Wifi displays. + * Starts scanning for available Wifi displays. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. *

+ * Calls to this method nest and must be matched by an equal number of calls to + * {@link #stopWifiDisplayScan()}. + *

+ * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. + *

+ * + * @hide + */ + public void startWifiDisplayScan() { + mGlobal.startWifiDisplayScan(); + } + + /** + * Stops scanning for available Wifi displays. + *

* Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. *

* * @hide */ - public void scanWifiDisplays() { - mGlobal.scanWifiDisplays(); + public void stopWifiDisplayScan() { + mGlobal.stopWifiDisplayScan(); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 936a0867fd83a014083f436647c262b5df143341..34174301ac8eb3a75fd4e47728c4a8e4822aab79 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -72,6 +72,8 @@ public final class DisplayManagerGlobal { private final SparseArray mDisplayInfoCache = new SparseArray(); private int[] mDisplayIdCache; + private int mWifiDisplayScanNestCount; + private DisplayManagerGlobal(IDisplayManager dm) { mDm = dm; } @@ -267,11 +269,32 @@ public final class DisplayManagerGlobal { } } - public void scanWifiDisplays() { - try { - mDm.scanWifiDisplays(); - } catch (RemoteException ex) { - Log.e(TAG, "Failed to scan for Wifi displays.", ex); + public void startWifiDisplayScan() { + synchronized (mLock) { + if (mWifiDisplayScanNestCount++ == 0) { + registerCallbackIfNeededLocked(); + try { + mDm.startWifiDisplayScan(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to scan for Wifi displays.", ex); + } + } + } + } + + public void stopWifiDisplayScan() { + synchronized (mLock) { + if (--mWifiDisplayScanNestCount == 0) { + try { + mDm.stopWifiDisplayScan(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to scan for Wifi displays.", ex); + } + } else if (mWifiDisplayScanNestCount < 0) { + Log.wtf(TAG, "Wifi display scan nest count became negative: " + + mWifiDisplayScanNestCount); + mWifiDisplayScanNestCount = 0; + } } } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 6b2c887639bd34229bc6d2a48b53582cd30b90a2..68eb13fb6d58db52db6e2351f3b4ffa3e7385563 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -29,11 +29,14 @@ interface IDisplayManager { void registerCallback(in IDisplayManagerCallback callback); - // No permissions required. - void scanWifiDisplays(); + // Requires CONFIGURE_WIFI_DISPLAY permission. + // The process must have previously registered a callback. + void startWifiDisplayScan(); - // Requires CONFIGURE_WIFI_DISPLAY permission to connect to an unknown device. - // No permissions required to connect to a known device. + // Requires CONFIGURE_WIFI_DISPLAY permission. + void stopWifiDisplayScan(); + + // Requires CONFIGURE_WIFI_DISPLAY permission. void connectWifiDisplay(String address); // No permissions required. @@ -45,6 +48,12 @@ interface IDisplayManager { // Requires CONFIGURE_WIFI_DISPLAY permission. void forgetWifiDisplay(String address); + // Requires CONFIGURE_WIFI_DISPLAY permission. + void pauseWifiDisplay(); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + void resumeWifiDisplay(); + // No permissions required. WifiDisplayStatus getWifiDisplayStatus(); @@ -55,10 +64,4 @@ interface IDisplayManager { // No permissions required but must be same Uid as the creator. void releaseVirtualDisplay(in IBinder token); - - // Requires CONFIGURE_WIFI_DISPLAY permission. - void pauseWifiDisplay(); - - // Requires CONFIGURE_WIFI_DISPLAY permission. - void resumeWifiDisplay(); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index c78a973ceeb0fc06387d03f08bea90a077db981b..70c8750623fbcf797df8fb22bc64f94b10527836 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -395,6 +395,8 @@ public class ConnectivityManager { private final IConnectivityManager mService; + private final String mPackageName; + /** * Tests if a given integer represents a valid network type. * @param networkType the type to be tested @@ -802,7 +804,7 @@ public class ConnectivityManager { public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) { byte[] address = hostAddress.getAddress(); try { - return mService.requestRouteToHostAddress(networkType, address); + return mService.requestRouteToHostAddress(networkType, address, mPackageName); } catch (RemoteException e) { return false; } @@ -898,8 +900,9 @@ public class ConnectivityManager { /** * {@hide} */ - public ConnectivityManager(IConnectivityManager service) { + public ConnectivityManager(IConnectivityManager service, String packageName) { mService = checkNotNull(service, "missing IConnectivityManager"); + mPackageName = checkNotNull(packageName, "missing package name"); } /** {@hide} */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index c1da2e32aa009aa6c08c1e3c24223a58aa4e95c7..4bca7fefbc44b18c4ab2e740438c0d0ea41acae7 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -71,9 +71,9 @@ interface IConnectivityManager int stopUsingNetworkFeature(int networkType, in String feature); - boolean requestRouteToHost(int networkType, int hostAddress); + boolean requestRouteToHost(int networkType, int hostAddress, String packageName); - boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress); + boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName); boolean getMobileDataEnabled(); void setMobileDataEnabled(boolean enabled); diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index c1065145dbf27dd666b4794cc16bcad67598c1c0..21352bf73f66b0e639a31fbe5394f1c9c9836a1e 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -566,6 +566,17 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { return false; } + + public void setInternalDataEnable(boolean enabled) { + if (DBG) log("setInternalDataEnable: E enabled=" + enabled); + final AsyncChannel channel = mDataConnectionTrackerAc; + if (channel != null) { + channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE, + enabled ? DctConstants.ENABLED : DctConstants.DISABLED); + } + if (VDBG) log("setInternalDataEnable: X enabled=" + enabled); + } + @Override public void setUserDataEnable(boolean enabled) { if (DBG) log("setUserDataEnable: E enabled=" + enabled); diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index d7dc7f5eac11a1a16332a314bd1924bf32a9b886..7385dff3ff8076eee3388aabb3fc8ed02dfad44e 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -151,9 +151,10 @@ public class VpnService extends Service { } /** - * Protect a socket from VPN connections. The socket will be bound to the - * current default network interface, so its traffic will not be forwarded - * through VPN. This method is useful if some connections need to be kept + * Protect a socket from VPN connections. After protecting, data sent + * through this socket will go directly to the underlying network, + * so its traffic will not be forwarded through the VPN. + * This method is useful if some connections need to be kept * outside of VPN. For example, a VPN tunnel should protect itself if its * destination is covered by VPN routes. Otherwise its outgoing packets * will be sent back to the VPN interface and cause an infinite loop. This diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index d4a30064707ee5ea415def9a79c1905054cb75de..26e09b63b8a1996009f655636ed9ae9a377b365c 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -610,7 +610,7 @@ public abstract class AsyncTask { * still running. Each call to this method will trigger the execution of * {@link #onProgressUpdate} on the UI thread. * - * {@link #onProgressUpdate} will note be called if the task has been + * {@link #onProgressUpdate} will not be called if the task has been * canceled. * * @param values The progress values to update the UI with. diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index c3313c53a5ee5d785120d60b2d70d3153a073ac7..b2e0b29c644ef66871b0ae5e35d7e8c2e8b9dc9c 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -49,7 +49,7 @@ public class Build { /** The manufacturer of the product/hardware. */ public static final String MANUFACTURER = getString("ro.product.manufacturer"); - /** The brand (e.g., carrier) the software is customized for, if any. */ + /** The consumer-visible brand with which the product/hardware will be associated, if any. */ public static final String BRAND = getString("ro.product.brand"); /** The end-user-visible name for the end product. */ diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 21b8ae57e36a46156c92d5815e2a4fd8ec5b14b2..f65b6ba79cd8727472915e6220c55dd38c2c45a3 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -420,7 +420,7 @@ interface INetworkManagementService /** * Clear a user range from being associated with an interface. */ - void clearDnsInterfaceForUidRange(int uid_start, int uid_end); + void clearDnsInterfaceForUidRange(String iface, int uid_start, int uid_end); /** * Clear the mappings from pid to Dns interface and from uid range to Dns interface. diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 6650fcaaa6a7d79ccd6b7f82e37053dac37a2fb6..5d55143b97eda8446c889e1982de4b3db95477d3 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -21,11 +21,11 @@ import android.content.Context; /** * Class that operates the vibrator on the device. *

- * If your process exits, any vibration you started with will stop. + * If your process exits, any vibration you started will stop. *

* * To obtain an instance of the system vibrator, call - * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument. + * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument. */ public abstract class Vibrator { /** diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 7ddfa874155bf7a90dfa8ff4ff73c2bd8d4b671c..2ab5a91a7013e9719fd366a596494567329db858 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -33,6 +33,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.util.TypedValue; import android.util.Xml; import android.view.LayoutInflater; @@ -124,6 +125,8 @@ public abstract class PreferenceActivity extends ListActivity implements PreferenceManager.OnPreferenceTreeClickListener, PreferenceFragment.OnPreferenceStartFragmentCallback { + private static final String TAG = "PreferenceActivity"; + // Constants for state save/restore private static final String HEADERS_TAG = ":android:headers"; private static final String CUR_HEADER_TAG = ":android:cur_header"; @@ -521,9 +524,7 @@ public abstract class PreferenceActivity extends ListActivity implements int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0); int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0); - // Restore from headers only if they are supported which - // is in multi-pane mode. - if (savedInstanceState != null && !mSinglePane) { + if (savedInstanceState != null) { // We are restarting from a previous saved state; used that to // initialize, instead of starting fresh. ArrayList
headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG); diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java index b615600d33ae83319f81e420f24009f715bffab2..806a89d8481ff356355d74a8e08c65a4eefcfeba 100644 --- a/core/java/android/print/PrinterCapabilitiesInfo.java +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -475,6 +475,12 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @param colorModes The color mode bit mask. * @param defaultColorMode The default color mode. * @return This builder. + *

+ * Note: On platform version 19 (Kitkat) specifying + * only PrintAttributes#COLOR_MODE_MONOCHROME leads to a print spooler + * crash. Hence, you should declare either both color modes or + * PrintAttributes#COLOR_MODE_COLOR. + *

* * @throws IllegalArgumentException If color modes contains an invalid * mode bit or if the default color mode is invalid. diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b16df28fbb3fbe7cdf2093d59c7e7cd94dba3012..0863368408bae44c6601e934859ed654702e6094 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -18,6 +18,7 @@ package android.provider; import android.accounts.Account; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentResolver; @@ -40,6 +41,7 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Pair; import android.view.View; +import android.widget.Toast; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -7981,7 +7983,7 @@ public final class ContactsContract { // Trigger with obtained rectangle Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode, excludeMimes); - context.startActivity(intent); + startActivityWithErrorToast(context, intent); } /** @@ -8014,7 +8016,16 @@ public final class ContactsContract { String[] excludeMimes) { Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode, excludeMimes); - context.startActivity(intent); + startActivityWithErrorToast(context, intent); + } + + private static void startActivityWithErrorToast(Context context, Intent intent) { + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available, + Toast.LENGTH_SHORT).show(); + } } } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 39775b625dc4160b4eb122a14d09b41d9a816b49..6519f7ee81e05ac00641f72511b5426b6065405c 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -257,8 +257,6 @@ public final class DocumentsContract { * {@link #MIME_TYPE_DIR}. * * @see #COLUMN_FLAGS - * @see DocumentsContract#createDocument(ContentResolver, Uri, String, - * String) * @see DocumentsProvider#createDocument(String, String, String) */ public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 04f3f0aad84dc1d1f5588a8612983f9e5e054777..3810eb32ca9442ea34c6e9ec2bdf6ccaa49e6b92 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5139,6 +5139,12 @@ public final class Settings { */ public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule"; + /** + * Used to select TCP's default initial receiver window size in segments - defaults to a build config value + * @hide + */ + public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd"; + /** * Used to disable Tethering on a device - defaults to true * @hide diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 5ef86b1a5456dd26cf739a9f10d74729ebeb095a..f34e746adf6f75f1bee2e94faa93cb2f224cc982 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -512,7 +512,7 @@ public class Time { *
      * Time time = new Time();
      * time.set(4, 10, 2007);  // set the date to Nov 4, 2007, 12am
-     * time.normalize();       // this sets isDst = 1
+     * time.normalize(false);       // this sets isDst = 1
      * time.monthDay += 1;     // changes the date to Nov 5, 2007, 12am
      * millis = time.toMillis(false);   // millis is Nov 4, 2007, 11pm
      * millis = time.toMillis(true);    // millis is Nov 5, 2007, 12am
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index d6e116f21e3feeed8fd53e0ec353c567183ec9ad..dab853a7fe2776a61dc902dbb0b7b6002c96b9ff 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -44,7 +44,7 @@ import com.android.internal.util.ArrayUtils;
  * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
  * keyAt(int) with ascending values of the index will return the
  * keys in ascending order, or the values corresponding to the keys in ascending
- * order in the case of valueAt(int).

+ * order in the case of valueAt(int).

*/ public class LongSparseArray implements Cloneable { private static final Object DELETED = new Object(); diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index 87d868b02b44adf56763a026fbeef3000032e16e..66548997ea7356a7977531954419ad16cffb7a70 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -39,7 +39,7 @@ import java.util.Arrays; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * keyAt(int) with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of valueAt(int).

+ * order in the case of valueAt(int).

* * @hide */ diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 6e168a8b4ccd7d5b612a03658eac4cdccde740d8..46d9d451c6fa58aa1f1375db4754efbef2bfe8cd 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -44,7 +44,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * keyAt(int) with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of valueAt(int).

+ * order in the case of valueAt(int).

*/ public class SparseArray implements Cloneable { private static final Object DELETED = new Object(); diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 68487e391710142699ce0bc0cdd622afe43e5a27..905dcb018b9695a88ef73e143808bf8f30f9f7d6 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -38,7 +38,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * keyAt(int) with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of valueAt(int).

+ * order in the case of valueAt(int).

*/ public class SparseBooleanArray implements Cloneable { /** diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 0835cb02e032d30bbc7a0df10cf09191ed54263b..4f5ca078d1e498c14021d2e6c20499377c867266 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -37,7 +37,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * keyAt(int) with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of valueAt(int).

+ * order in the case of valueAt(int).

*/ public class SparseIntArray implements Cloneable { private int[] mKeys; diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index 62c1c0d9b6fa0f84a6f45c02879759626c6a9320..39fc8a37cc3022a02d8f670e2a8221a76435da58 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -37,7 +37,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * keyAt(int) with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of valueAt(int).

+ * order in the case of valueAt(int).

*/ public class SparseLongArray implements Cloneable { private int[] mKeys; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bdf4141f059923ac36ac88f9995b5ba0110c2851..ef60755531162c85a68f76b8a8c3328d0009dcd7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6543,7 +6543,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_filterTouchesWhenObscured */ public void setFilterTouchesWhenObscured(boolean enabled) { - setFlags(enabled ? 0 : FILTER_TOUCHES_WHEN_OBSCURED, + setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED : 0, FILTER_TOUCHES_WHEN_OBSCURED); } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index c3f064fdfd4ae8bfa4742b7f91e2f2dcbeff2dd9..e67659c2485e0532e80d1a5deff581cfdb2f510d 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -212,6 +212,14 @@ public class ViewConfiguration { */ private static final int OVERFLING_DISTANCE = 6; + /** + * Configuration values for overriding {@link #hasPermanentMenuKey()} behavior. + * These constants must match the definition in res/values/config.xml. + */ + private static final int HAS_PERMANENT_MENU_KEY_AUTODETECT = 0; + private static final int HAS_PERMANENT_MENU_KEY_TRUE = 1; + private static final int HAS_PERMANENT_MENU_KEY_FALSE = 2; + private final int mEdgeSlop; private final int mFadingEdgeLength; private final int mMinimumFlingVelocity; @@ -296,12 +304,31 @@ public class ViewConfiguration { mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f); if (!sHasPermanentMenuKeySet) { - IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); - try { - sHasPermanentMenuKey = !wm.hasNavigationBar(); - sHasPermanentMenuKeySet = true; - } catch (RemoteException ex) { - sHasPermanentMenuKey = false; + final int configVal = res.getInteger( + com.android.internal.R.integer.config_overrideHasPermanentMenuKey); + + switch (configVal) { + default: + case HAS_PERMANENT_MENU_KEY_AUTODETECT: { + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + sHasPermanentMenuKey = !wm.hasNavigationBar(); + sHasPermanentMenuKeySet = true; + } catch (RemoteException ex) { + sHasPermanentMenuKey = false; + } + } + break; + + case HAS_PERMANENT_MENU_KEY_TRUE: + sHasPermanentMenuKey = true; + sHasPermanentMenuKeySet = true; + break; + + case HAS_PERMANENT_MENU_KEY_FALSE: + sHasPermanentMenuKey = false; + sHasPermanentMenuKeySet = true; + break; } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3fd2aa9de924af3113ac87f827fc58856b702108..5b2a4527aa90d6525d86380a0c552b7c1491156c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -232,8 +232,6 @@ public final class ViewRootImpl implements ViewParent, InputStage mFirstInputStage; InputStage mFirstPostImeInputStage; - boolean mFlipControllerFallbackKeys; - boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; @@ -370,8 +368,6 @@ public final class ViewRootImpl implements ViewParent, mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); mChoreographer = Choreographer.getInstance(); - mFlipControllerFallbackKeys = - context.getResources().getBoolean(R.bool.flip_controller_fallback_keys); PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mAttachInfo.mScreenOn = powerManager.isScreenOn(); @@ -2912,11 +2908,8 @@ public final class ViewRootImpl implements ViewParent, mView.dispatchConfigurationChanged(config); } } - - mFlipControllerFallbackKeys = - mContext.getResources().getBoolean(R.bool.flip_controller_fallback_keys); } - + /** * Return true if child is an ancestor of parent, (or equal to the parent). */ @@ -3985,7 +3978,6 @@ public final class ViewRootImpl implements ViewParent, private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler(); private final SyntheticTouchNavigationHandler mTouchNavigation = new SyntheticTouchNavigationHandler(); - private final SyntheticKeyHandler mKeys = new SyntheticKeyHandler(); public SyntheticInputStage() { super(null); @@ -4008,12 +4000,7 @@ public final class ViewRootImpl implements ViewParent, mTouchNavigation.process(event); return FINISH_HANDLED; } - } else if (q.mEvent instanceof KeyEvent) { - if (mKeys.process((KeyEvent) q.mEvent)) { - return FINISH_HANDLED; - } } - return FORWARD; } @@ -4843,63 +4830,6 @@ public final class ViewRootImpl implements ViewParent, }; } - final class SyntheticKeyHandler { - - public boolean process(KeyEvent event) { - // In some locales (like Japan) controllers use B for confirm and A for back, rather - // than vice versa, so we need to special case this here since the input system itself - // is not locale-aware. - int keyCode; - switch(event.getKeyCode()) { - case KeyEvent.KEYCODE_BUTTON_A: - case KeyEvent.KEYCODE_BUTTON_C: - case KeyEvent.KEYCODE_BUTTON_X: - case KeyEvent.KEYCODE_BUTTON_Z: - keyCode = mFlipControllerFallbackKeys ? - KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_DPAD_CENTER; - break; - case KeyEvent.KEYCODE_BUTTON_B: - case KeyEvent.KEYCODE_BUTTON_Y: - keyCode = mFlipControllerFallbackKeys ? - KeyEvent.KEYCODE_DPAD_CENTER : KeyEvent.KEYCODE_BACK; - break; - case KeyEvent.KEYCODE_BUTTON_THUMBL: - case KeyEvent.KEYCODE_BUTTON_THUMBR: - case KeyEvent.KEYCODE_BUTTON_START: - case KeyEvent.KEYCODE_BUTTON_1: - case KeyEvent.KEYCODE_BUTTON_2: - case KeyEvent.KEYCODE_BUTTON_3: - case KeyEvent.KEYCODE_BUTTON_4: - case KeyEvent.KEYCODE_BUTTON_5: - case KeyEvent.KEYCODE_BUTTON_6: - case KeyEvent.KEYCODE_BUTTON_7: - case KeyEvent.KEYCODE_BUTTON_8: - case KeyEvent.KEYCODE_BUTTON_9: - case KeyEvent.KEYCODE_BUTTON_10: - case KeyEvent.KEYCODE_BUTTON_11: - case KeyEvent.KEYCODE_BUTTON_12: - case KeyEvent.KEYCODE_BUTTON_13: - case KeyEvent.KEYCODE_BUTTON_14: - case KeyEvent.KEYCODE_BUTTON_15: - case KeyEvent.KEYCODE_BUTTON_16: - keyCode = KeyEvent.KEYCODE_DPAD_CENTER; - break; - case KeyEvent.KEYCODE_BUTTON_SELECT: - case KeyEvent.KEYCODE_BUTTON_MODE: - keyCode = KeyEvent.KEYCODE_MENU; - default: - return false; - } - - enqueueInputEvent(new KeyEvent(event.getDownTime(), event.getEventTime(), - event.getAction(), keyCode, event.getRepeatCount(), event.getMetaState(), - event.getDeviceId(), event.getScanCode(), - event.getFlags() | KeyEvent.FLAG_FALLBACK, event.getSource())); - return true; - } - - } - /** * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index f0e6677d016b7eb9afc0dbad09d4cbc7b6d7a96a..52f9c0b480cad4adcf222414abcc50fcd7bf1e6f 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -311,7 +311,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie lp.type = LayoutParams.TYPE_VOLUME_OVERLAY; lp.width = LayoutParams.WRAP_CONTENT; lp.height = LayoutParams.WRAP_CONTENT; - lp.privateFlags |= LayoutParams.PRIVATE_FLAG_FORCE_SHOW_NAV_BAR; window.setAttributes(lp); window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 0ce4da5c4dc5c184039cc8368e4595211bf126dd..53a4c0d0df511dd86280ec46b4d1bb8c5c691b1c 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1062,13 +1062,6 @@ public interface WindowManager extends ViewManager { * {@hide} */ public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010; - /** - * Special flag for the volume overlay: force the window manager out of "hide nav bar" - * mode while the window is on screen. - * - * {@hide} */ - public static final int PRIVATE_FLAG_FORCE_SHOW_NAV_BAR = 0x00000020; - /** * Never animate position changes of the window. * diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 5bc39f1584084bfc45b5b470c226c7f9cf636e2c..d53bb74c81ab76a68889d6a08a54c22c1c42fc76 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -685,6 +685,15 @@ public class WebView extends AbsoluteLayout getFactory().getStatics().setPlatformNotificationsEnabled(false); } + /** + * Used only by internal tests to free up memory. + * + * @hide + */ + public static void freeMemoryForTests() { + getFactory().getStatics().freeMemoryForTests(); + } + /** * Informs WebView of the network state. This is used to set * the JavaScript property window.navigator.isOnline and diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 9d9d88292c1adcb775d9e2c2a3d1066377b2e0d8..e391aafd546587b38f6409edd23033990fe786a0 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -49,6 +49,11 @@ public interface WebViewFactoryProvider { */ String getDefaultUserAgent(Context context); + /** + * Used for tests only. + */ + void freeMemoryForTests(); + /** * Implements the API method: * {@link android.webkit.WebView#setWebContentsDebuggingEnabled(boolean) } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 092f4749e0404ab741392db73a8bd3ce3bc118f3..25a43a6a2969c0528d2e7badac80b1d27c9420a6 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -723,7 +723,7 @@ public abstract class AbsListView extends AdapterView implements Te * * @param view The view whose scroll state is being reported * - * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE}, + * @param scrollState The current scroll state. One of * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. */ public void onScrollStateChanged(AbsListView view, int scrollState); @@ -2076,22 +2076,23 @@ public abstract class AbsListView extends AdapterView implements Te protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; + final int childCount = getChildCount(); if (changed) { - int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } - if (mFastScroller != null && (mItemCount != mOldItemCount || mDataChanged)) { - mFastScroller.onItemCountChanged(mItemCount); - } - layoutChildren(); mInLayout = false; mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; + + // TODO: Move somewhere sane. This doesn't belong in onLayout(). + if (mFastScroller != null) { + mFastScroller.onItemCountChanged(getChildCount(), mItemCount); + } } /** @@ -2228,26 +2229,34 @@ public abstract class AbsListView extends AdapterView implements Te Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); isScrap[0] = false; - View scrapView; - scrapView = mRecycler.getTransientStateView(position); - if (scrapView == null) { - scrapView = mRecycler.getScrapView(position); - } + // Check whether we have a transient state view. Attempt to re-bind the + // data and discard the view if we fail. + final View transientView = mRecycler.getTransientStateView(position); + if (transientView != null) { + final LayoutParams params = (LayoutParams) transientView.getLayoutParams(); - View child; - if (scrapView != null) { - child = mAdapter.getView(position, scrapView, this); + // If the view type hasn't changed, attempt to re-bind the data. + if (params.viewType == mAdapter.getItemViewType(position)) { + final View updatedView = mAdapter.getView(position, transientView, this); - if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + // If we failed to re-bind the data, scrap the obtained view. + if (updatedView != transientView) { + mRecycler.addScrapView(updatedView, position); + } } + // Scrap view implies temporary detachment. + isScrap[0] = true; + return transientView; + } + + final View scrapView = mRecycler.getScrapView(position); + final View child = mAdapter.getView(position, scrapView, this); + if (scrapView != null) { if (child != scrapView) { + // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); - if (mCacheColorHint != 0) { - child.setDrawingCacheBackgroundColor(mCacheColorHint); - } } else { isScrap[0] = true; @@ -2259,16 +2268,14 @@ public abstract class AbsListView extends AdapterView implements Te child.dispatchFinishTemporaryDetach(); } - } else { - child = mAdapter.getView(position, null, this); + } - if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - } + if (mCacheColorHint != 0) { + child.setDrawingCacheBackgroundColor(mCacheColorHint); + } - if (mCacheColorHint != 0) { - child.setDrawingCacheBackgroundColor(mCacheColorHint); - } + if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } if (mAdapterHasStableIds) { @@ -6562,12 +6569,8 @@ public abstract class AbsListView extends AdapterView implements Te } } } - if (mTransientStateViews != null) { - mTransientStateViews.clear(); - } - if (mTransientStateViewsById != null) { - mTransientStateViewsById.clear(); - } + + clearTransientStateViews(); } /** @@ -6634,14 +6637,26 @@ public abstract class AbsListView extends AdapterView implements Te } /** - * Dump any currently saved views with transient state. + * Dumps and fully detaches any currently saved views with transient + * state. */ void clearTransientStateViews() { - if (mTransientStateViews != null) { - mTransientStateViews.clear(); + final SparseArray viewsByPos = mTransientStateViews; + if (viewsByPos != null) { + final int N = viewsByPos.size(); + for (int i = 0; i < N; i++) { + removeDetachedView(viewsByPos.valueAt(i), false); + } + viewsByPos.clear(); } - if (mTransientStateViewsById != null) { - mTransientStateViewsById.clear(); + + final LongSparseArray viewsById = mTransientStateViewsById; + if (viewsById != null) { + final int N = viewsById.size(); + for (int i = 0; i < N; i++) { + removeDetachedView(viewsById.valueAt(i), false); + } + viewsById.clear(); } } @@ -6766,44 +6781,48 @@ public abstract class AbsListView extends AdapterView implements Te if (victim != null) { final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) victim.getLayoutParams(); - int whichScrap = lp.viewType; + final int whichScrap = lp.viewType; activeViews[i] = null; - final boolean scrapHasTransientState = victim.hasTransientState(); - if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) { - // Do not move views that should be ignored - if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && - scrapHasTransientState) { + if (victim.hasTransientState()) { + // Store views with transient state for later use. + victim.dispatchStartTemporaryDetach(); + + if (mAdapter != null && mAdapterHasStableIds) { + if (mTransientStateViewsById == null) { + mTransientStateViewsById = new LongSparseArray(); + } + long id = mAdapter.getItemId(mFirstActivePosition + i); + mTransientStateViewsById.put(id, victim); + } else if (!mDataChanged) { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArray(); + } + mTransientStateViews.put(mFirstActivePosition + i, victim); + } else if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { + // The data has changed, we can't keep this view. removeDetachedView(victim, false); } - if (scrapHasTransientState) { - if (mAdapter != null && mAdapterHasStableIds) { - if (mTransientStateViewsById == null) { - mTransientStateViewsById = new LongSparseArray(); - } - long id = mAdapter.getItemId(mFirstActivePosition + i); - mTransientStateViewsById.put(id, victim); - } else { - if (mTransientStateViews == null) { - mTransientStateViews = new SparseArray(); - } - mTransientStateViews.put(mFirstActivePosition + i, victim); - } + } else if (!shouldRecycleViewType(whichScrap)) { + // Discard non-recyclable views except headers/footers. + if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { + removeDetachedView(victim, false); + } + } else { + // Store everything else on the appropriate scrap heap. + if (multipleScraps) { + scrapViews = mScrapViews[whichScrap]; } - continue; - } - if (multipleScraps) { - scrapViews = mScrapViews[whichScrap]; - } - victim.dispatchStartTemporaryDetach(); - lp.scrappedFromPosition = mFirstActivePosition + i; - scrapViews.add(victim); + victim.dispatchStartTemporaryDetach(); + lp.scrappedFromPosition = mFirstActivePosition + i; + scrapViews.add(victim); - victim.setAccessibilityDelegate(null); - if (hasListener) { - mRecyclerListener.onMovedToScrapHeap(victim); + victim.setAccessibilityDelegate(null); + if (hasListener) { + mRecyclerListener.onMovedToScrapHeap(victim); + } } } } @@ -6812,8 +6831,10 @@ public abstract class AbsListView extends AdapterView implements Te } /** - * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews. - * (This can happen if an adapter does not recycle its views). + * Makes sure that the size of mScrapViews does not exceed the size of + * mActiveViews, which can happen if an adapter does not recycle its + * views. Removes cached transient state views that no longer have + * transient state. */ private void pruneScrapViews() { final int maxViews = mActiveViews.length; @@ -6829,20 +6850,25 @@ public abstract class AbsListView extends AdapterView implements Te } } - if (mTransientStateViews != null) { - for (int i = 0; i < mTransientStateViews.size(); i++) { - final View v = mTransientStateViews.valueAt(i); + final SparseArray transViewsByPos = mTransientStateViews; + if (transViewsByPos != null) { + for (int i = 0; i < transViewsByPos.size(); i++) { + final View v = transViewsByPos.valueAt(i); if (!v.hasTransientState()) { - mTransientStateViews.removeAt(i); + removeDetachedView(v, false); + transViewsByPos.removeAt(i); i--; } } } - if (mTransientStateViewsById != null) { - for (int i = 0; i < mTransientStateViewsById.size(); i++) { - final View v = mTransientStateViewsById.valueAt(i); + + final LongSparseArray transViewsById = mTransientStateViewsById; + if (transViewsById != null) { + for (int i = 0; i < transViewsById.size(); i++) { + final View v = transViewsById.valueAt(i); if (!v.hasTransientState()) { - mTransientStateViewsById.removeAt(i); + removeDetachedView(v, false); + transViewsById.removeAt(i); i--; } } diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java index 0957ab4670e613484377b95f2a579edaa69b48c5..e90b4608a88836b065b09b44455a799998934cb8 100644 --- a/core/java/android/widget/CalendarView.java +++ b/core/java/android/widget/CalendarView.java @@ -1210,35 +1210,38 @@ public class CalendarView extends FrameLayout { child = (WeekView) view.getChildAt(offset); } - // Find out which month we're moving into - int month; - if (mIsScrollingUp) { - month = child.getMonthOfFirstWeekDay(); - } else { - month = child.getMonthOfLastWeekDay(); - } - - // And how it relates to our current highlighted month - int monthDiff; - if (mCurrentMonthDisplayed == 11 && month == 0) { - monthDiff = 1; - } else if (mCurrentMonthDisplayed == 0 && month == 11) { - monthDiff = -1; - } else { - monthDiff = month - mCurrentMonthDisplayed; - } - - // Only switch months if we're scrolling away from the currently - // selected month - if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) { - Calendar firstDay = child.getFirstDay(); + if (child != null) { + // Find out which month we're moving into + int month; if (mIsScrollingUp) { - firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK); + month = child.getMonthOfFirstWeekDay(); } else { - firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK); + month = child.getMonthOfLastWeekDay(); + } + + // And how it relates to our current highlighted month + int monthDiff; + if (mCurrentMonthDisplayed == 11 && month == 0) { + monthDiff = 1; + } else if (mCurrentMonthDisplayed == 0 && month == 11) { + monthDiff = -1; + } else { + monthDiff = month - mCurrentMonthDisplayed; + } + + // Only switch months if we're scrolling away from the currently + // selected month + if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) { + Calendar firstDay = child.getFirstDay(); + if (mIsScrollingUp) { + firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK); + } else { + firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK); + } + setMonthDisplayed(firstDay); } - setMonthDisplayed(firstDay); } + mPreviousScrollPosition = currScroll; mPreviousScrollState = mCurrentScrollState; } diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 01ac8fdba6d0ee3a0cd64a58e3a0e424e26c3dd5..4379bf62336ff87718a1d64b3878aba80cb5d8da 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -224,6 +224,8 @@ class FastScroller { mHasPendingDrag = false; } }; + private int mOldItemCount; + private int mOldChildCount; /** * Used to delay hiding fast scroll decorations. @@ -248,6 +250,8 @@ class FastScroller { public FastScroller(AbsListView listView) { mList = listView; mOverlay = listView.getOverlay(); + mOldItemCount = listView.getCount(); + mOldChildCount = listView.getChildCount(); final Context context = listView.getContext(); mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); @@ -258,6 +262,7 @@ class FastScroller { final ImageView trackImage = new ImageView(context); mTrackImage = trackImage; + updateLongList(mOldChildCount, mOldItemCount); int width = 0; // Add track to overlay if it has an image. @@ -445,20 +450,23 @@ class FastScroller { updateLayout(); } - public void onItemCountChanged(int totalItemCount) { - final int visibleItemCount = mList.getChildCount(); - final boolean hasMoreItems = totalItemCount - visibleItemCount > 0; - if (hasMoreItems && mState != STATE_DRAGGING) { - final int firstVisibleItem = mList.getFirstVisiblePosition(); - setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount)); - } + public void onItemCountChanged(int childCount, int itemCount) { + if (mOldItemCount != itemCount || mOldChildCount != childCount) { + mOldItemCount = itemCount; + mOldChildCount = childCount; - updateLongList(visibleItemCount, totalItemCount); + final boolean hasMoreItems = itemCount - childCount > 0; + if (hasMoreItems && mState != STATE_DRAGGING) { + final int firstVisibleItem = mList.getFirstVisiblePosition(); + setThumbPos(getPosFromItemCount(firstVisibleItem, childCount, itemCount)); + } + + updateLongList(childCount, itemCount); + } } - private void updateLongList(int visibleItemCount, int totalItemCount) { - final boolean longList = visibleItemCount > 0 - && totalItemCount / visibleItemCount >= MIN_PAGES; + private void updateLongList(int childCount, int itemCount) { + final boolean longList = childCount > 0 && itemCount / childCount >= MIN_PAGES; if (mLongList != longList) { mLongList = longList; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 7daf798194fb87bc28c4f9b2d42a8cefd90e1bee..f05179bb5883af172fc266ae5f2d5a09531fe319 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -563,7 +563,7 @@ public class ImageView extends View { } /** Return the view's optional matrix. This is applied to the - view's drawable when it is drawn. If there is not matrix, + view's drawable when it is drawn. If there is no matrix, this method will return an identity matrix. Do not change this matrix in place but make a copy. If you want a different matrix applied to the drawable, diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 5663959bdfe34ee4d947d7d718d3dbfc111a2669..be20d2d836f8b17e057fff1e8eaa80bf7a2bba28 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1330,7 +1330,7 @@ public class PopupWindow { /** * Updates the state of the popup window, if it is currently being displayed, - * from the currently set state. This include: + * from the currently set state. This includes: * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)}, * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)}, * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}. diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 5392a96715bf3590f986439b13663a592a3b52ab..6a369a60a3242612a706e36388088b374e81841f 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -308,6 +308,11 @@ public class ProgressBar extends View { mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); a.recycle(); + + // If not explicitly specified this view is important for accessibility. + if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } } /** diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 62afd2eef1e1b03ab52eef14036cc8a3fa6d5c67..bdaaa0164ceb554bf17609ea81e3b521777e1151 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -266,7 +266,7 @@ public class ShareActionProvider extends ActionProvider { * Intent shareIntent = new Intent(Intent.ACTION_SEND); * shareIntent.setType("image/*"); * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg")); - * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
+ * shareIntent.putExtra(Intent.EXTRA_STREAM, uri)); * * @param shareIntent The share intent. * diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index b204dfd0c032b3ac4c773302d402da53bec786a4..1cda631a498a952e87c9a60c76f7734d0decbef1 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -731,10 +731,14 @@ public class SpellChecker implements SpellCheckerSessionListener { } } - if (scheduleOtherSpellCheck) { + if (scheduleOtherSpellCheck && wordStart <= end) { // Update range span: start new spell check from last wordStart setRangeSpan(editable, wordStart, end); } else { + if (DBG && scheduleOtherSpellCheck) { + Log.w(TAG, "Trying to schedule spellcheck for invalid region, from " + + wordStart + " to " + end); + } removeRangeSpan(editable); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3f35875f314eb2228d8d6621089b5986fd4e4012..5ece0160b841c7e42f8c08651dfd01af2f274970 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8667,6 +8667,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.onRtlPropertiesChanged(layoutDirection); mTextDir = getTextDirectionHeuristic(); + + if (mLayout != null) { + checkForRelayout(); + } } TextDirectionHeuristic getTextDirectionHeuristic() { diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index a87992aeb341c92188271660792d892de0c84cd7..a13a1cbce2901b210ac9f00444ffe6f78e5bda46 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -1004,7 +1004,7 @@ public final class ProcessStats implements Parcelable { for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { ProcessState ps = pkgState.mProcesses.valueAt(iproc); if (ps.isInUse() || ps.mCommonProcess.isInUse()) { - pkgState.mProcesses.valueAt(iproc).resetSafely(now); + ps.resetSafely(now); } else { pkgState.mProcesses.valueAt(iproc).makeDead(); pkgState.mProcesses.removeAt(iproc); @@ -1013,7 +1013,7 @@ public final class ProcessStats implements Parcelable { for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { ServiceState ss = pkgState.mServices.valueAt(isvc); if (ss.isInUse()) { - pkgState.mServices.valueAt(isvc).resetSafely(now); + ss.resetSafely(now); } else { pkgState.mServices.removeAt(isvc); } @@ -3014,7 +3014,7 @@ public final class ProcessStats implements Parcelable { } public boolean isInUse() { - return mOwner != null; + return mOwner != null || mRestarting; } void add(ServiceState other) { diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index ab871fb80a59da39f36e68ba4abb8a7377eb11bc..942995b51a83e8d6f767c57d9b2bc601e0679ebb 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -372,23 +372,25 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); mAppearingPackages = pkgList; - mChangeType = PACKAGE_TEMPORARY_CHANGE; + mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) + ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE; mSomePackagesChanged = true; if (pkgList != null) { onPackagesAvailable(pkgList); for (int i=0; i parent, View view, int position, long id) { - mMenu.performItemAction(mAdapter.getItem(position), 0); + mMenu.performItemAction(mAdapter.getItem(position), this, 0); } @Override diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index aff697ad580c6a12931400dc0e3e30d0b92d94e7..195a00d9eca7efe60a7b185b167371dcc4bf3da9 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -247,11 +247,17 @@ public class MenuBuilder implements Menu { startDispatchingItemsChanged(); } - private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) { + private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu, + MenuPresenter preferredPresenter) { if (mPresenters.isEmpty()) return false; boolean result = false; + // Try the preferred presenter first. + if (preferredPresenter != null) { + result = preferredPresenter.onSubMenuSelected(subMenu); + } + for (WeakReference ref : mPresenters) { final MenuPresenter presenter = ref.get(); if (presenter == null) { @@ -865,6 +871,10 @@ public class MenuBuilder implements Menu { } public boolean performItemAction(MenuItem item, int flags) { + return performItemAction(item, null, flags); + } + + public boolean performItemAction(MenuItem item, MenuPresenter preferredPresenter, int flags) { MenuItemImpl itemImpl = (MenuItemImpl) item; if (itemImpl == null || !itemImpl.isEnabled()) { @@ -889,7 +899,7 @@ public class MenuBuilder implements Menu { if (providerHasSubMenu) { provider.onPrepareSubMenu(subMenu); } - invoked |= dispatchSubMenuSelected(subMenu); + invoked |= dispatchSubMenuSelected(subMenu, preferredPresenter); if (!invoked) close(true); } else { if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) { diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 7143a8abb056bf58bf967abf7251767476a8aa58..d97a945bbef88224dba536d384c99f495c155cb7 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -227,7 +227,7 @@ static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width, do { *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]); } while (--width != 0); - ctable->unlockColors(false); + ctable->unlockColors(); } static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width, @@ -240,7 +240,7 @@ static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width, *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); } while (--width != 0); - ctable->unlockColors(false); + ctable->unlockColors(); } static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, @@ -253,7 +253,7 @@ static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); } while (--width != 0); - ctable->unlockColors(false); + ctable->unlockColors(); } // can return NULL @@ -456,10 +456,16 @@ static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE; } -static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, - jboolean hasAlpha) { +static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasAlpha, jboolean isPremul) { SkBitmap* bitmap = reinterpret_cast(bitmapHandle); - bitmap->setIsOpaque(!hasAlpha); + if (!hasAlpha) { + bitmap->setAlphaType(kOpaque_SkAlphaType); + } else if (isPremul) { + bitmap->setAlphaType(kPremul_SkAlphaType); + } else { + bitmap->setAlphaType(kUnpremul_SkAlphaType); + } } static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { @@ -561,14 +567,14 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->writeInt32(bitmap->rowBytes()); p->writeInt32(density); - if (bitmap->getConfig() == SkBitmap::kIndex8_Config) { + if (bitmap->config() == SkBitmap::kIndex8_Config) { SkColorTable* ctable = bitmap->getColorTable(); if (ctable != NULL) { int count = ctable->count(); p->writeInt32(count); memcpy(p->writeInplace(count * sizeof(SkPMColor)), ctable->lockColors(), count * sizeof(SkPMColor)); - ctable->unlockColors(false); + ctable->unlockColors(); } else { p->writeInt32(0); // indicate no ctable } @@ -800,7 +806,7 @@ static JNINativeMethod gBitmapMethods[] = { { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, { "nativeConfig", "(J)I", (void*)Bitmap_config }, { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, - { "nativeSetHasAlpha", "(JZ)V", (void*)Bitmap_setHasAlpha }, + { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied}, { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, { "nativeCreateFromParcel", diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 44bf7f802c9d76a4e7a45a1b9f0bb11b1c3ce76e..2b9a5c4a049c2ab82efbd92ef04c3c6d0b785484 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -126,12 +127,18 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream, int sampleSize, bool ditherImage) { + SkImageInfo bitmapInfo; + if (!bitmap->asImageInfo(&bitmapInfo)) { + ALOGW("bitmap has unknown configuration so no memory has been allocated"); + return NULL; + } + SkImageRef* pr; // only use ashmem for large images, since mmaps come at a price if (bitmap->getSize() >= 32 * 1024) { - pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); + pr = new SkImageRef_ashmem(bitmapInfo, stream, sampleSize); } else { - pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); + pr = new SkImageRef_GlobalPool(bitmapInfo, stream, sampleSize); } pr->setDitherImage(ditherImage); bitmap->setPixelRef(pr)->unref(); @@ -159,7 +166,7 @@ public: virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { // accounts for scale in final allocation, using eventual size and config const int bytesPerPixel = SkBitmap::ComputeBytesPerPixel( - configForScaledOutput(bitmap->getConfig())); + configForScaledOutput(bitmap->config())); const int requestedSize = bytesPerPixel * int(bitmap->width() * mScale + 0.5f) * int(bitmap->height() * mScale + 0.5f); @@ -193,8 +200,15 @@ public: return false; } + SkImageInfo bitmapInfo; + if (!bitmap->asImageInfo(&bitmapInfo)) { + ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration"); + return false; + } + // Create a new pixelref with the new ctable that wraps the previous pixelref - SkPixelRef* pr = new AndroidPixelRef(*static_cast(mPixelRef), ctable); + SkPixelRef* pr = new AndroidPixelRef(*static_cast(mPixelRef), + bitmapInfo, bitmap->rowBytes(), ctable); bitmap->setPixelRef(pr)->unref(); // since we're already allocated, we lockPixels right away @@ -403,8 +417,11 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // TODO: avoid copying when scaled size equals decodingBitmap size SkBitmap::Config config = configForScaledOutput(decodingBitmap.config()); - outputBitmap->setConfig(config, scaledWidth, scaledHeight); - outputBitmap->setIsOpaque(decodingBitmap.isOpaque()); + // FIXME: If the alphaType is kUnpremul and the image has alpha, the + // colors may not be correct, since Skia does not yet support drawing + // to/from unpremultiplied bitmaps. + outputBitmap->setConfig(config, scaledWidth, scaledHeight, 0, + decodingBitmap.alphaType()); if (!outputBitmap->allocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } @@ -416,7 +433,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } SkPaint paint; - paint.setFilterBitmap(true); + paint.setFilterLevel(SkPaint::kLow_FilterLevel); SkCanvas canvas(*outputBitmap); canvas.scale(sx, sy); @@ -472,6 +489,12 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding bitmapCreateFlags, ninePatchChunk, layoutBounds, -1); } +// Need to buffer enough input to be able to rewind as much as might be read by a decoder +// trying to determine the stream's format. Currently the most is 64, read by +// SkImageDecoder_libwebp. +// FIXME: Get this number from SkImageDecoder +#define BYTES_TO_BUFFER 64 + static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, jobject padding, jobject options) { @@ -479,11 +502,8 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA SkAutoTUnref stream(CreateJavaInputStreamAdaptor(env, is, storage)); if (stream.get()) { - // Need to buffer enough input to be able to rewind as much as might be read by a decoder - // trying to determine the stream's format. Currently the most is 64, read by - // SkImageDecoder_libwebp. - // FIXME: Get this number from SkImageDecoder - SkAutoTUnref bufferedStream(SkFrontBufferedStream::Create(stream, 64)); + SkAutoTUnref bufferedStream( + SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER)); SkASSERT(bufferedStream.get() != NULL); // for now we don't allow purgeable with java inputstreams bitmap = doDecode(env, bufferedStream, padding, options, false, false); @@ -504,27 +524,48 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi return nullObjectReturn("fstat return -1"); } - bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); - bool isShareable = optionsShareable(env, bitmapFactoryOptions); - bool weOwnTheFD = false; - if (isPurgeable && isShareable) { - int newFD = ::dup(descriptor); - if (-1 != newFD) { - weOwnTheFD = true; - descriptor = newFD; - } + // Restore the descriptor's offset on exiting this function. + AutoFDSeek autoRestore(descriptor); + + FILE* file = fdopen(descriptor, "r"); + if (file == NULL) { + return nullObjectReturn("Could not open file"); } - SkAutoTUnref data(SkData::NewFromFD(descriptor)); - if (data.get() == NULL) { - return nullObjectReturn("NewFromFD failed in nativeDecodeFileDescriptor"); + SkAutoTUnref fileStream(new SkFILEStream(file, + SkFILEStream::kCallerRetains_Ownership)); + + SkAutoTUnref stream; + + // Retain the old behavior of allowing purgeable if both purgeable and + // shareable are set to true. + bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions) + && optionsShareable(env, bitmapFactoryOptions); + if (isPurgeable) { + // Copy the stream, so the image can be decoded multiple times without + // continuing to modify the original file descriptor. + // Copy beginning from the current position. + const size_t fileSize = fileStream->getLength() - fileStream->getPosition(); + void* buffer = sk_malloc_flags(fileSize, 0); + if (buffer == NULL) { + return nullObjectReturn("Could not make a copy for ashmem"); + } + + SkAutoTUnref data(SkData::NewFromMalloc(buffer, fileSize)); + + if (fileStream->read(buffer, fileSize) != fileSize) { + return nullObjectReturn("Could not read the file."); + } + + stream.reset(new SkMemoryStream(data)); + } else { + // Use a buffered stream. Although an SkFILEStream can be rewound, this + // ensures that SkImageDecoder::Factory never rewinds beyond the + // current position of the file descriptor. + stream.reset(SkFrontBufferedStream::Create(fileStream, BYTES_TO_BUFFER)); } - SkAutoTUnref stream(new SkMemoryStream(data)); - /* Allow purgeable iff we own the FD, i.e., in the puregeable and - shareable case. - */ - return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); + return doDecode(env, stream, padding, bitmapFactoryOptions, isPurgeable); } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, @@ -543,7 +584,9 @@ static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, } else { // since we know we'll be done with the asset when we return, we can // just use a simple wrapper - stream = new AssetStreamAdaptor(asset); + stream = new AssetStreamAdaptor(asset, + AssetStreamAdaptor::kNo_OwnAsset, + AssetStreamAdaptor::kNo_HasMemoryBase); } SkAutoUnref aur(stream); return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable); diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index e7d2422eeb9b61aabbabffe684f3e31760757927..0d67b071a5abee1b9df982ad45d1c09c0eb80536 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -29,7 +29,6 @@ #include "CreateJavaOutputStreamAdaptor.h" #include "Utils.h" #include "JNIHelp.h" -#include "SkTScopedPtr.h" #include #include "android_util_Binder.h" @@ -76,7 +75,7 @@ private: int fHeight; }; -static jobject createBitmapRegionDecoder(JNIEnv* env, SkStream* stream) { +static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) { SkImageDecoder* decoder = SkImageDecoder::Factory(stream); int width, height; if (NULL == decoder) { @@ -108,7 +107,7 @@ static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray b For now we just always copy the array's data if isShareable. */ AutoJavaByteArray ar(env, byteArray); - SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true); + SkStreamRewindable* stream = new SkMemoryStream(ar.ptr() + offset, length, true); jobject brd = createBitmapRegionDecoder(env, stream); SkSafeUnref(stream); // the decoder now holds a reference @@ -216,7 +215,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, region.fRight = start_x + width; region.fBottom = start_y + height; SkBitmap* bitmap = NULL; - SkTScopedPtr adb; + SkAutoTDelete adb; if (tileBitmap != NULL) { // Re-use bitmap. @@ -247,7 +246,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, } // detach bitmap from its autodeleter, since we want to own it now - adb.release(); + adb.detach(); JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); jbyteArray buff = allocator->getStorageObjAndReset(); diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 11089da274ae66d0e81cf0487c83085eae193ffe..f6ab39c43fd3a6d218cb1e2842ab95e1ae42a52c 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -549,7 +549,7 @@ public: if (paint) { filteredPaint = *paint; } - filteredPaint.setFilterBitmap(true); + filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint); } else { canvas->drawBitmap(*bitmap, left_, top_, paint); @@ -564,7 +564,7 @@ public: if (paint) { filteredPaint = *paint; } - filteredPaint.setFilterBitmap(true); + filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint); @@ -587,7 +587,7 @@ public: if (paint) { filteredPaint = *paint; } - filteredPaint.setFilterBitmap(true); + filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint); } else { canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint); diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 2f4fd2934c82fe155ad5ba4018fc94aaee90ccb4..5d951ca17c38cc5ecd60fcaed0016f82a349d1d6 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -357,6 +357,18 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// +// Assert that bitmap's SkAlphaType is consistent with isPremultiplied. +static void assert_premultiplied(const SkBitmap& bitmap, bool isPremultiplied) { + // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is + // irrelevant. This just tests to ensure that the SkAlphaType is not + // opposite of isPremultiplied. + if (isPremultiplied) { + SkASSERT(bitmap.alphaType() != kUnpremul_SkAlphaType); + } else { + SkASSERT(bitmap.alphaType() != kPremul_SkAlphaType); + } +} + jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, int bitmapCreateFlags, jbyteArray ninepatch, jintArray layoutbounds, int density) { @@ -365,6 +377,10 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buff bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; + // The caller needs to have already set the alpha type properly, so the + // native SkBitmap stays in sync with the Java Bitmap. + assert_premultiplied(*bitmap, isPremultiplied); + jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, reinterpret_cast(bitmap), buffer, bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied, @@ -382,6 +398,10 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreat void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap, bool isPremultiplied) { + // The caller needs to have already set the alpha type properly, so the + // native SkBitmap stays in sync with the Java Bitmap. + assert_premultiplied(*bitmap, isPremultiplied); + env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID, bitmap->width(), bitmap->height(), isPremultiplied); } @@ -424,8 +444,9 @@ static JNIEnv* vm2env(JavaVM* vm) /////////////////////////////////////////////////////////////////////////////// -AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, - SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable, (storageObj == NULL)), +AndroidPixelRef::AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, + size_t rowBytes, jbyteArray storageObj, SkColorTable* ctable) : + SkMallocPixelRef(info, storage, rowBytes, ctable, (storageObj == NULL)), fWrappedPixelRef(NULL) { SkASSERT(storage); SkASSERT(env); @@ -440,13 +461,13 @@ AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteA // If storageObj is NULL, the memory was NOT allocated on the Java heap fOnJavaHeap = (storageObj != NULL); - } -AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, SkColorTable* ctable) : - SkMallocPixelRef(wrappedPixelRef.getAddr(), wrappedPixelRef.getSize(), ctable, false), +AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info, + size_t rowBytes, SkColorTable* ctable) : + SkMallocPixelRef(info, wrappedPixelRef.getAddr(), rowBytes, ctable, false), fWrappedPixelRef(wrappedPixelRef.fWrappedPixelRef ? - wrappedPixelRef.fWrappedPixelRef : &wrappedPixelRef) + wrappedPixelRef.fWrappedPixelRef : &wrappedPixelRef) { SkASSERT(fWrappedPixelRef); SkSafeRef(fWrappedPixelRef); @@ -548,6 +569,14 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, "bitmap size exceeds 32bits"); return NULL; } + + SkImageInfo bitmapInfo; + if (!bitmap->asImageInfo(&bitmapInfo)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "unknown bitmap configuration"); + return NULL; + } + size_t size = size64.get32(); jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime, gVMRuntime_newNonMovableArray, @@ -561,7 +590,8 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, return NULL; } SkASSERT(addr); - SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable); + SkPixelRef* pr = new AndroidPixelRef(env, bitmapInfo, (void*) addr, + bitmap->rowBytes(), arrayObj, ctable); bitmap->setPixelRef(pr)->unref(); // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index f4590b956d78a7c7e4715307464ff00ab761441f..cb154aaf28e58b6ef9e40b9d277b1b6fba604b48 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -57,6 +57,7 @@ public: /** Create a java Bitmap object given the native bitmap (required) and optional storage array (may be null). + bitmap's SkAlphaType must already be in sync with bitmapCreateFlags. */ static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, int bitmapCreateFlags, jbyteArray ninepatch, jintArray layoutbounds, int density = -1); @@ -64,6 +65,9 @@ public: static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags, jbyteArray ninepatch, int density = -1); + /** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in + sync with isPremultiplied + */ static void reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap, bool isPremultiplied); @@ -88,15 +92,16 @@ public: class AndroidPixelRef : public SkMallocPixelRef { public: - AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, - SkColorTable* ctable); + AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, size_t rowBytes, + jbyteArray storageObj, SkColorTable* ctable); /** * Creates an AndroidPixelRef that wraps (and refs) another to reuse/share * the same storage and java byte array refcounting, yet have a different * color table. */ - AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, SkColorTable* ctable); + AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info, + size_t rowBytes, SkColorTable* ctable); virtual ~AndroidPixelRef(); diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp index 0040b6f15d7ad011da87a24589643cecc9b6bcea..461f7232b3b1b8a720a99c6102c83441c5b0a96f 100644 --- a/core/jni/android/graphics/Movie.cpp +++ b/core/jni/android/graphics/Movie.cpp @@ -85,7 +85,9 @@ static void movie_draw(JNIEnv* env, jobject movie, jobject canvas, static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jlong native_asset) { android::Asset* asset = reinterpret_cast(native_asset); if (asset == NULL) return NULL; - SkAutoTUnref stream (new android::AssetStreamAdaptor(asset)); + SkAutoTUnref stream (new android::AssetStreamAdaptor(asset, + android::AssetStreamAdaptor::kNo_OwnAsset, + android::AssetStreamAdaptor::kNo_HasMemoryBase)); SkMovie* moov = SkMovie::DecodeStream(stream.get()); return create_jmovie(env, moov); } diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp index 51392abef84c2680ece31705bf0f4177729a2de7..5daa1ad6e2ef82dc8e257ada4f1b724d2430a3ab 100644 --- a/core/jni/android/graphics/NinePatchPeeker.cpp +++ b/core/jni/android/graphics/NinePatchPeeker.cpp @@ -40,15 +40,14 @@ bool NinePatchPeeker::peek(const char tag[], const void* data, size_t length) { // now update our host to force index or 32bit config // 'cause we don't want 565 predithered, since as a 9patch, we know // we will be stretched, and therefore we want to dither afterwards. - static const SkBitmap::Config gNo565Pref[] = { - SkBitmap::kIndex8_Config, - SkBitmap::kIndex8_Config, - SkBitmap::kARGB_8888_Config, - SkBitmap::kARGB_8888_Config, - SkBitmap::kARGB_8888_Config, - SkBitmap::kARGB_8888_Config, - }; - fHost->setPrefConfigTable(gNo565Pref); + SkImageDecoder::PrefConfigTable table; + table.fPrefFor_8Index_NoAlpha_src = SkBitmap::kIndex8_Config; + table.fPrefFor_8Index_YesAlpha_src = SkBitmap::kIndex8_Config; + table.fPrefFor_8Gray_src = SkBitmap::kARGB_8888_Config; + table.fPrefFor_8bpc_NoAlpha_src = SkBitmap::kARGB_8888_Config; + table.fPrefFor_8bpc_YesAlpha_src = SkBitmap::kARGB_8888_Config; + + fHost->setPrefConfigTable(table); } else if (strcmp("npLb", tag) == 0 && length == sizeof(int) * 4) { fLayoutBounds = new int[4]; memcpy(fLayoutBounds, data, sizeof(int) * 4); diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 25234813a8373bc880e27506efc5bcd94de2d916..dc97c2281c85920d386c60483f64855bb0b20811 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -155,7 +155,8 @@ public: static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) { NPE_CHECK_RETURN_VOID(env, paint); - GraphicsJNI::getNativePaint(env, paint)->setFilterBitmap(filterBitmap); + GraphicsJNI::getNativePaint(env, paint)->setFilterLevel( + filterBitmap ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel); } static void setDither(JNIEnv* env, jobject paint, jboolean dither) { @@ -609,7 +610,7 @@ public: return 0; } } - jfloat advancesArray[count]; + jfloat* advancesArray = new jfloat[count]; jfloat totalAdvance = 0; TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags, @@ -618,6 +619,7 @@ public: if (advances != NULL) { env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray); } + delete [] advancesArray; return totalAdvance; } diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp index bcf127399fcb0ceae3de5c552711c65eba773f4e..912968a9da3f975aea5856226b87d75c1bb63f49 100644 --- a/core/jni/android/graphics/Region.cpp +++ b/core/jni/android/graphics/Region.cpp @@ -214,7 +214,7 @@ static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) SkRegion* region = new SkRegion; size_t size = p->readInt32(); - region->readFromMemory(p->readInplace(size)); + region->readFromMemory(p->readInplace(size), size); return reinterpret_cast(region); } diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index e2c78b1ba412d392ccddd40aea673e8bdf8038af..5db083a1296ade196e43de2b504fd639f5d61b31 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -20,6 +20,7 @@ #include "TextLayoutCache.h" #include "TextLayout.h" +#include "SkGlyphCache.h" #include "SkTypeface_android.h" #include "HarfBuzzNGFaceSkia.h" #include @@ -757,8 +758,8 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* conte outPos->add(ypos); totalAdvance += xAdvance; - // TODO: consider using glyph cache - const SkGlyph& metrics = mShapingPaint.getGlyphMetrics(glyphId, NULL); + SkAutoGlyphCache autoCache(mShapingPaint, NULL, NULL); + const SkGlyph& metrics = autoCache.getCache()->getGlyphIDMetrics(glyphId); outBounds->join(xpos + metrics.fLeft, ypos + metrics.fTop, xpos + metrics.fLeft + metrics.fWidth, ypos + metrics.fTop + metrics.fHeight); diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index d096c2a2c17911413973c3fee3ead6ef1be5df8c..81646250f4ebf955a07254ecd168df71136d19f8 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -4,6 +4,7 @@ #include "GraphicsJNI.h" #include "SkStream.h" #include "SkTypeface.h" +#include "Utils.h" #include #include @@ -77,65 +78,6 @@ static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) { return static_cast(face->style()); } -class AssetStream : public SkStream { -public: - AssetStream(Asset* asset, bool hasMemoryBase) : fAsset(asset) - { - fMemoryBase = hasMemoryBase ? fAsset->getBuffer(false) : NULL; - } - - virtual ~AssetStream() - { - delete fAsset; - } - - virtual const void* getMemoryBase() - { - return fMemoryBase; - } - - virtual bool rewind() - { - off64_t pos = fAsset->seek(0, SEEK_SET); - return pos != (off64_t)-1; - } - - virtual size_t read(void* buffer, size_t size) - { - ssize_t amount; - - if (NULL == buffer) - { - if (0 == size) // caller is asking us for our total length - return fAsset->getLength(); - - // asset->seek returns new total offset - // we want to return amount that was skipped - - off64_t oldOffset = fAsset->seek(0, SEEK_CUR); - if (-1 == oldOffset) - return 0; - off64_t newOffset = fAsset->seek(size, SEEK_CUR); - if (-1 == newOffset) - return 0; - - amount = newOffset - oldOffset; - } - else - { - amount = fAsset->read(buffer, size); - } - - if (amount < 0) - amount = 0; - return amount; - } - -private: - Asset* fAsset; - const void* fMemoryBase; -}; - static jlong Typeface_createFromAsset(JNIEnv* env, jobject, jobject jassetMgr, jstring jpath) { @@ -154,7 +96,9 @@ static jlong Typeface_createFromAsset(JNIEnv* env, jobject, return NULL; } - SkStream* stream = new AssetStream(asset, true); + SkStream* stream = new AssetStreamAdaptor(asset, + AssetStreamAdaptor::kYes_OwnAsset, + AssetStreamAdaptor::kYes_HasMemoryBase); SkTypeface* face = SkTypeface::CreateFromStream(stream); // SkTypeFace::CreateFromStream calls ref() on the stream, so we // need to unref it here or it won't be freed later on diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp index b7d1f3ab220fd240c90ef91c35795bd15a87e1fc..eb416cbe7ad2c4b114829976dd01b1e69cd215ab 100644 --- a/core/jni/android/graphics/Utils.cpp +++ b/core/jni/android/graphics/Utils.cpp @@ -19,6 +19,21 @@ using namespace android; +AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset, OwnAsset ownAsset, + HasMemoryBase hasMemoryBase) + : fAsset(asset) + , fMemoryBase(kYes_HasMemoryBase == hasMemoryBase ? + asset->getBuffer(false) : NULL) + , fOwnAsset(ownAsset) +{ +} + +AssetStreamAdaptor::~AssetStreamAdaptor() { + if (kYes_OwnAsset == fOwnAsset) { + delete fAsset; + } +} + bool AssetStreamAdaptor::rewind() { off64_t pos = fAsset->seek(0, SEEK_SET); if (pos == (off64_t)-1) { diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h index a1ac72a0b92bf4392a99e03351a62176f23ef3e2..b90593cdc4b092e8b67470d4adecd58813aa2ecb 100644 --- a/core/jni/android/graphics/Utils.h +++ b/core/jni/android/graphics/Utils.h @@ -28,16 +28,36 @@ namespace android { class AssetStreamAdaptor : public SkStreamRewindable { public: - AssetStreamAdaptor(Asset* a) : fAsset(a) {} + // Enum passed to constructor. If set to kYes_OwnAsset, + // the passed in Asset will be deleted upon destruction. + enum OwnAsset { + kYes_OwnAsset, + kNo_OwnAsset, + }; + + // Enum passed to constructor. If set to kYes_HasMemoryBase, + // getMemoryBase will return the Asset's buffer. + enum HasMemoryBase { + kYes_HasMemoryBase, + kNo_HasMemoryBase, + }; + + AssetStreamAdaptor(Asset*, OwnAsset, HasMemoryBase); + ~AssetStreamAdaptor(); + virtual bool rewind(); virtual size_t read(void* buffer, size_t size); virtual bool hasLength() const { return true; } virtual size_t getLength() const; virtual bool isAtEnd() const; + virtual const void* getMemoryBase() { return fMemoryBase; } + virtual SkStreamRewindable* duplicate() const; private: - Asset* fAsset; + Asset* fAsset; + const void* const fMemoryBase; + const OwnAsset fOwnAsset; }; /** diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp index 48367ffb464fce8169cae96963339ebd8fa3ff70..a17f328439e0278d204f64f6ca334e50efe42744 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -632,7 +632,7 @@ static jint util_getInternalFormat(JNIEnv *env, jclass clazz, SkBitmap const * nativeBitmap = (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); const SkBitmap& bitmap(*nativeBitmap); - SkBitmap::Config config = bitmap.getConfig(); + SkBitmap::Config config = bitmap.config(); return getInternalFormat(config); } @@ -642,7 +642,7 @@ static jint util_getType(JNIEnv *env, jclass clazz, SkBitmap const * nativeBitmap = (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); const SkBitmap& bitmap(*nativeBitmap); - SkBitmap::Config config = bitmap.getConfig(); + SkBitmap::Config config = bitmap.config(); return getType(config); } @@ -653,7 +653,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, SkBitmap const * nativeBitmap = (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); const SkBitmap& bitmap(*nativeBitmap); - SkBitmap::Config config = bitmap.getConfig(); + SkBitmap::Config config = bitmap.config(); if (internalformat < 0) { internalformat = getInternalFormat(config); } @@ -681,7 +681,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, SkColorTable* ctable = bitmap.getColorTable(); memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); memcpy(pixels, p, size); - ctable->unlockColors(false); + ctable->unlockColors(); glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data); free(data); } else { @@ -702,7 +702,7 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz, SkBitmap const * nativeBitmap = (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); const SkBitmap& bitmap(*nativeBitmap); - SkBitmap::Config config = bitmap.getConfig(); + SkBitmap::Config config = bitmap.config(); if (format < 0) { format = getInternalFormat(config); if (format == GL_PALETTE8_RGBA8_OES) diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index c2d4ec0c5b1ba4176df85b733829a23eec157e7b..ab6c1e0c3d9a000e8ef241b8fae728b867c72a0e 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -179,7 +180,8 @@ static jboolean nativeIsConsumerRunningBehind(JNIEnv* env, jclass clazz, jlong n static inline SkBitmap::Config convertPixelFormat(PixelFormat format) { /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then we can map to SkBitmap::kARGB_8888_Config, and optionally call - bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator) + bitmap.setAlphaType(kOpaque_SkAlphaType) on the resulting SkBitmap + (as an accelerator) */ switch (format) { case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config; @@ -235,7 +237,7 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr); if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) { - bitmap.setIsOpaque(true); + bitmap.setAlphaType(kOpaque_SkAlphaType); } if (outBuffer.width > 0 && outBuffer.height > 0) { bitmap.setPixels(outBuffer.bits); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index bd530954c283b6df6eb2c2604c1df0d148d57e12..ed84a6443e203651ec43221d4a7d295724de56d1 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -61,51 +61,21 @@ static struct { class ScreenshotPixelRef : public SkPixelRef { public: - ScreenshotPixelRef(SkColorTable* ctable) { - fCTable = ctable; - SkSafeRef(ctable); + ScreenshotPixelRef(const SkImageInfo& info, ScreenshotClient* screenshot) : + SkPixelRef(info), + mScreenshot(screenshot) { setImmutable(); } virtual ~ScreenshotPixelRef() { - SkSafeUnref(fCTable); - } - - status_t update(const sp& display, int width, int height, - int minLayer, int maxLayer, bool allLayers) { - status_t res = (width > 0 && height > 0) - ? (allLayers - ? mScreenshot.update(display, width, height) - : mScreenshot.update(display, width, height, minLayer, maxLayer)) - : mScreenshot.update(display); - if (res != NO_ERROR) { - return res; - } - - return NO_ERROR; - } - - uint32_t getWidth() const { - return mScreenshot.getWidth(); - } - - uint32_t getHeight() const { - return mScreenshot.getHeight(); - } - - uint32_t getStride() const { - return mScreenshot.getStride(); - } - - uint32_t getFormat() const { - return mScreenshot.getFormat(); + delete mScreenshot; } protected: // overrides from SkPixelRef virtual void* onLockPixels(SkColorTable** ct) { - *ct = fCTable; - return (void*)mScreenshot.getPixels(); + *ct = NULL; + return (void*)mScreenshot->getPixels(); } virtual void onUnlockPixels() { @@ -113,8 +83,7 @@ protected: SK_DECLARE_UNFLATTENABLE_OBJECT() private: - ScreenshotClient mScreenshot; - SkColorTable* fCTable; + ScreenshotClient* mScreenshot; typedef SkPixelRef INHERITED; }; @@ -147,19 +116,6 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong nativeObject) { ctrl->decStrong((void *)nativeCreate); } -static inline SkBitmap::Config convertPixelFormat(PixelFormat format) { - /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then - we can map to SkBitmap::kARGB_8888_Config, and optionally call - bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator) - */ - switch (format) { - case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config; - case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config; - case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config; - default: return SkBitmap::kNo_Config; - } -} - static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj, jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) { sp displayToken = ibinderForJavaObject(env, displayTokenObj); @@ -167,26 +123,50 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject display return NULL; } - ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL); - if (pixels->update(displayToken, width, height, - minLayer, maxLayer, allLayers) != NO_ERROR) { - delete pixels; + ScreenshotClient* screenshot = new ScreenshotClient(); + status_t res = (width > 0 && height > 0) + ? (allLayers + ? screenshot->update(displayToken, width, height) + : screenshot->update(displayToken, width, height, minLayer, maxLayer)) + : screenshot->update(displayToken); + if (res != NO_ERROR) { + delete screenshot; return NULL; } - uint32_t w = pixels->getWidth(); - uint32_t h = pixels->getHeight(); - uint32_t s = pixels->getStride(); - uint32_t f = pixels->getFormat(); - ssize_t bpr = s * android::bytesPerPixel(f); + SkImageInfo screenshotInfo; + screenshotInfo.fWidth = screenshot->getWidth(); + screenshotInfo.fHeight = screenshot->getHeight(); - SkBitmap* bitmap = new SkBitmap(); - bitmap->setConfig(convertPixelFormat(f), w, h, bpr); - if (f == PIXEL_FORMAT_RGBX_8888) { - bitmap->setIsOpaque(true); + switch (screenshot->getFormat()) { + case PIXEL_FORMAT_RGBX_8888: { + screenshotInfo.fColorType = kRGBA_8888_SkColorType; + screenshotInfo.fAlphaType = kIgnore_SkAlphaType; + break; + } + case PIXEL_FORMAT_RGBA_8888: { + screenshotInfo.fColorType = kRGBA_8888_SkColorType; + screenshotInfo.fAlphaType = kPremul_SkAlphaType; + break; + } + case PIXEL_FORMAT_RGB_565: { + screenshotInfo.fColorType = kRGB_565_SkColorType; + screenshotInfo.fAlphaType = kIgnore_SkAlphaType; + break; + } + default: { + delete screenshot; + return NULL; + } } - if (w > 0 && h > 0) { + // takes ownership of ScreenshotClient + ScreenshotPixelRef* pixels = new ScreenshotPixelRef(screenshotInfo, screenshot); + ssize_t rowBytes = screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat()); + + SkBitmap* bitmap = new SkBitmap(); + bitmap->setConfig(screenshotInfo, rowBytes); + if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) { bitmap->setPixelRef(pixels)->unref(); bitmap->lockPixels(); } else { diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index 54f9278792c7fae3fe41b8f1db9982eeb798eaa4..77ede33ca150c3ed2aa94991e0cbe60c28bcd9c4 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -27,6 +27,7 @@ #include #include +#include namespace android { @@ -162,7 +163,7 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject, bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount); if (buffer.format == WINDOW_FORMAT_RGBX_8888) { - bitmap.setIsOpaque(true); + bitmap.setAlphaType(kOpaque_SkAlphaType); } if (buffer.width > 0 && buffer.height > 0) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 83559288a812efbafaa71f84732755bf27b49751..8ec2d64185ecfaa0fb2cb8d6d22eb25af23a6ba5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1056,8 +1056,9 @@ android:permissionGroupFlags="personalInfo" android:priority="370" /> - + + android:protectionLevel="signature" /> @@ -1534,7 +1535,7 @@ @hide --> diff --git a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png index 458a2a66ce788990b018c0e204aa08d457dbdf55..e215b96f9301254a50f32daceee1eb7ff026f572 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png index 03b0d2ac71381da5b9a0268b2c080b7a038e3143..a014e918441b77a6c8d00af5bcd05aa1cfba66d4 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png index c91faa929c5532cc4d9a4de7040ca9233e3b326e..bb8bec19518535ee061bea34e0caf71792f5acb1 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png index 14c9183fb5b2110b7c2d00d8846130fa17f658fd..aa1737edf1562f723bab1b26f9f77fa11fbc2641 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png index b388d8686ac9c04c1489150c4372f8bf6099901f..2c1434bbd09f5b3b5f2aa5b942b3e37a837db4a9 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png index 76c132380dde16d1cd0d99c5e2f4d258d1438562..dbdce3ef2f5011112d92a982fc8bde9eef533fa0 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png index fd39f9d6aa17adf32801b97267a19ccf1a8a0031..110186495da93974c93aae1c1a10386cd93e1186 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png index c74727aabe219db3db2e2153f548370b5aa47b0a..e8e90697e55fbf0517c1f232c94c1385b0831757 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png index 826c9aefed7a0e9e159a133077e71681860a3b9b..8595158bed2c33f35ebc622f4d5ab1a9d4558504 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png index d0baec3fbaa9ddd74bb9d93a641aad811cfd7c31..14844d403a2679ae813eefbce6c75d4afc7cc0aa 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png index c60ff599decaf8fee27d3e752568ce323f6a6383..1565a29b8f477aed025a9a481facad6cabc048e7 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png index 75552ccf0ab86eec457884b9395d00a53adad952..9b8fe879d55d8d788b10acdc10ed1dc398adf11b 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_0.png b/core/res/res/drawable-hdpi/ic_notification_cast_0.png index a35f28177f0a5afd3be9ee342ef3a36a2efa279b..74f7dc0c1f489b75358c05dffba89fae54f499cc 100644 Binary files a/core/res/res/drawable-hdpi/ic_notification_cast_0.png and b/core/res/res/drawable-hdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_1.png b/core/res/res/drawable-hdpi/ic_notification_cast_1.png index 9f6e2adef029e019e248c91c3652ba58e2189545..c6d267d8c021e3422cb11e052fb65223b3a8e326 100644 Binary files a/core/res/res/drawable-hdpi/ic_notification_cast_1.png and b/core/res/res/drawable-hdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_2.png b/core/res/res/drawable-hdpi/ic_notification_cast_2.png index 737137a81340f11a16eae55abd00388a75d45df4..699b299ffb8b121f3450d695a7d652321afaa62f 100644 Binary files a/core/res/res/drawable-hdpi/ic_notification_cast_2.png and b/core/res/res/drawable-hdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_on.png b/core/res/res/drawable-hdpi/ic_notification_cast_on.png index ff2753ab68f5c2868d54f7df0204f023bcdb383a..3eaf13a8520c454571aafde802b5c94f2d034a7e 100644 Binary files a/core/res/res/drawable-hdpi/ic_notification_cast_on.png and b/core/res/res/drawable-hdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png index fa22d8244cf9fbea605f05183fa3c079ffb8d429..52e3a5a132367c49b0f29a87a1f52448c84ef9f5 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png index a686cd1cf2aa02b733e3cb34ed7f919f0f268c96..319c57e8f343f22b12799ab91937eac20ca6eb80 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_disabled_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png index 9d92648511ae15ae49d8ac65dc5470181b5f56f1..f98c0a85b53bb1102c2635556c136cc65e051bed 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png index 3e27fc8f2eb8281e1c70886a956f88319b8eb9d9..b74cdb5b1a93fc5adaf09bb4297cb7e13ebee15b 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png index 72b9e78956ec41cc22ade0fd54b2b6b9df6b8ca0..a6a4bd012e531dd568d34494315c3785760ebe25 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png index bd462a2b2ccc59601cd9fa451252fa0f6dc961dc..106fd3a97f94d0c255a126a1a093c32467f9f264 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png index 0a2cc89d535ad154e2335b2fab353d015ce63473..2c141ab8e692d84c608550dbe98b9c9ea26d1c9b 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png index d162503202acce3c9b7a25f2fd1360be4851df59..0b62d0b509ff5ed6c8ea06ac189f3479e44a1535 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png index 997e32b680b901d58453191e50c9ac7b68d3c93e..23442b06336b463c925d661e441f0dd9a48b5ea3 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png index d314967d38a1d6ce8f981a5411ee0f560daad962..42b329fad28e2dade39fb539e372d8d85bb3c108 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png index f15d7a9d77607beccd5a4fbef63bd9bde67ce75f..58ff50685aca12318b32ca89faca8e5fe28a6362 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png index 26d46f8b7b98074753717bbdeed4159d13cdbc35..25257f8b848dd86193da58141d8dff43037faa64 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_0.png b/core/res/res/drawable-mdpi/ic_notification_cast_0.png index d9cedbdeb3e5b1b434548b40cdbd5548cb4d956f..a51a3cbefd2f1aeecefb03ef292993a6ef34d2df 100644 Binary files a/core/res/res/drawable-mdpi/ic_notification_cast_0.png and b/core/res/res/drawable-mdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_1.png b/core/res/res/drawable-mdpi/ic_notification_cast_1.png index 414c67f17002b29e85815fab49921e4406994af5..e0813678a9f542e4a03774e44cedfaf0b28b4b29 100644 Binary files a/core/res/res/drawable-mdpi/ic_notification_cast_1.png and b/core/res/res/drawable-mdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_2.png b/core/res/res/drawable-mdpi/ic_notification_cast_2.png index 280a8880e5223411e91d83c9ebc8dfa7bca14f1b..a7f4de43f20315550c8e2e84c856ce4918ffc642 100644 Binary files a/core/res/res/drawable-mdpi/ic_notification_cast_2.png and b/core/res/res/drawable-mdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_on.png b/core/res/res/drawable-mdpi/ic_notification_cast_on.png index ab5f1d78608f3b4dc5e57867008539beae0fdbd9..42de8c4d5c10a9765a36a12dd9bd15c6bfd61749 100644 Binary files a/core/res/res/drawable-mdpi/ic_notification_cast_on.png and b/core/res/res/drawable-mdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png index 6b3157985ea56214f20902e7854f630cf0513ad7..7b6d48b6e41fc08e9c37f6f10e099ec8f503e830 100644 Binary files a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png and b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png differ diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png index df0121bb353f5b30b732b715a758ea6fcb3d5220..bafe8781c2b84df996d2b57c4af43be7a4680970 100644 Binary files a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png and b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png index 045eee0549d877202d31f9bafe958f6710613853..4119cffb3932b0e86c97752d30f826978b9b0b37 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png index 6e14e296336ab6d6e2fa46a66e159dc8891ec702..b629a5726b3b4ddc5b08c9969a430afb834f5497 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png index 121bbf6a46a0b5d7212a40a6e53460f59389c290..fe81128d5443dd57aadab3019957026e7cfe072b 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png index 468a0c36c07f1d92fc3a4209efca53754fe737a1..9b59eaf73976dd0f031d98061edf0bc55f78a0de 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png index 414a322e8b4904147d6bb304ec18dff599ca1645..1a513c1cd75e3bb814cd7e5a959badba0c06f27a 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png index 6088a48e4113244500576d43b38462460053b440..ff788032ea7b29eb884121c3392cb9e12587715b 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png index 363d7d4681ce4b2d16bf862172e5ba6b7d22be6f..4c4b624b9bdbfb32445ea743b18c2d778fb6e04a 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png index edf731e8fd4220581a6d12d16ed8ce8a5f485b0e..60f8c4dede13da2d6d995441fa4dd9e3c7d86a75 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png index 85cba7b80fc90e29e01c8551b20318e3ec7a8712..cdb2f30f0bcfaa23f817299888e638b2eaf28115 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png index e65ac319044bdbfd6d7f4aeee0a38985b9528660..97a10a37a29fd346635ae461eeb2245dc0f73b73 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png index d8e3e3aeae38888f6f8a8ae599e46c5fbb722e2f..a19a083706da90a7458ae4a12a0081cf96d53861 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png index 562dc9a4f2cf40fa7e3b0c3628d0f66a2eedce7e..db30613771caad583c918c600f87d5d04cf5b762 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png index 5fb23a06df3512f4c1e1eead5d7d242632027d42..818c1cdd7a261115b36031ef7a7e36162c010cb3 100644 Binary files a/core/res/res/drawable-xhdpi/ic_notification_cast_0.png and b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png index f01d17db0821ff88c1e50d3c5cffccd23c3be2ee..2a56e3131789828d6ef8911a539a39b1d651f47f 100644 Binary files a/core/res/res/drawable-xhdpi/ic_notification_cast_1.png and b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png index 4f4ba7f98bcd5aa1a92e3a1ad15f8f4a846fe94d..3515a76825e2c925ae0c3be1c60d69296a784a57 100644 Binary files a/core/res/res/drawable-xhdpi/ic_notification_cast_2.png and b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png index 38f15ddc416d02474901fa908de4f105eaa387a8..142065b4f5756b28a8f4690903420dd1f04e8e18 100644 Binary files a/core/res/res/drawable-xhdpi/ic_notification_cast_on.png and b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png index 418f322ce0f4fe236a65b335533110d5b33ea279..cbb4f4cf63da91433e17bcce6b258e5137cd686f 100644 Binary files a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png and b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png differ diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png index a5a59d4e61bafd6983b6c1f6879bd71c3d07e1cf..6d467f7ee6d55bfd8f8fe0345c4b289ca9a70576 100644 Binary files a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png and b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png index 178774ca179a17547bab9a1bada938da3d460dd1..6fad4a641e817bd0a0230ce306aa648e43cf9896 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png index 2dc2092f0418914b2f3d149e800257532c6cce15..865617c0fd0ee282f4ac5836140c76843d44049a 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png index 592ee8cdf3261d3e27f25e796658514f380b0bf0..44d98d56d2b54998a2ec3056c971fda540179ebf 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png index f0549e24188de13a686d28235670143edc960838..b5b29b020900dcee828dbabe545cdfaa915b2aeb 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png index 91268f5bdc81c5b99b6604255b50ea8f1f5a1f5c..c807b50ad076fa991e47af41121638841c8809c0 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png index 9d5436f8669914f8cc789daf8dc26da9b69d0051..3fc7188f31b40e0e2dfc9ed0bdd1b570ab6f221f 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png index 8e77483bce59a4943576d49e150f2ff2a1c219a8..d54f44acb84ba2a83eb3456513d27b577b93a4c9 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png index f396d22170f58d148e0148f940c74b87d467f51a..092fe8cc0dc500bd1df19111baea26adb1751281 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png index 260bab4f3959b48f8cc09322bdddf11e50ba056a..17c1d9945b1271ded0c6d9cc461ef6524046902c 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png index 2c9fb1da498a8e78caa99c3b77a9eb1303f94b9b..4fd5808648411de96cb2410eed8a8b51c55b06fa 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png index bdbd59c1264492a7bc401579797ecc7be5ac87c0..906401e6f03f53286347e6505764fddfea541dec 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png index f5c33dd210e5535ae5b31cc7edc6ea7cadc9c612..d29e563004083370e568440a3d4d9f62318222d5 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png index f5b16ed10e0e3e917e30fc04f031768613f4dd18..7ef0d3d0e3905158703e69da3b194e4e1af41de4 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png and b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png index 22efeec677f7c3502e494ee612eb962c2bb4493f..ed04beb7e48b18baa853ad483b269fd57be7b737 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png and b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png index e24cd970da16916deac9cdb4ac98250ff0902a7d..d62d27dd7a4c65941a8847f1813c76a96dcd461c 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png and b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png index da1a627be5bf158ac193c3295292434a6c4a6a89..d5626024b0a5656b14baddd7b885fde189d97a87 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png and b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/values-ja/bools.xml b/core/res/res/values-ja/bools.xml deleted file mode 100644 index 59cf744532eb5497e374a783f3dfe4d8148c6ea2..0000000000000000000000000000000000000000 --- a/core/res/res/values-ja/bools.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - true - - diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index 10a5d851fac30ff0cc70399bd870b1649eb0914b..18e4f2f9b3a82be1c4d98ca7852e031bd0ea69ea 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -26,5 +26,4 @@ true true true - false diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b34c792f5de0046b2da665a259587c33ea9572a6..a3b81327b738ab43a51d7f78e71efd28a0237309 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -352,6 +352,10 @@ false + + WS + false @@ -1304,4 +1308,12 @@ 333 353 + + + 0 diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 012fb8372b8e4820374a4ddf3a54bdbec4ea4155..538003f198cd86489c8e6cec2ac648a605b1264e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -625,9 +625,9 @@ reroute outgoing calls - Allows the app to process - outgoing calls and change the number to be dialed. This permission allows - the app to monitor, redirect, or prevent outgoing calls. + Allows the app to see the + number being dialed during an outgoing call with the option to redirect + the call to a different number or abort the call altogether. receive text messages (SMS) @@ -932,13 +932,13 @@ Allows the app to retrieve collected application operation statistics. Not for use by normal apps. - + modify app ops statistics Allows the app to modify collected application operation statistics. Not for use by normal apps. - + control system backup and restore @@ -2255,6 +2255,10 @@ Other + + No application found to view this contact. + Type PIN code diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6d90973c44f24557532a9fd2c5f7cf5885aca760..120027629a64776ee25f52a35bd9f0e5524f32e9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -288,7 +288,6 @@ - @@ -319,6 +318,7 @@ + @@ -478,6 +478,7 @@ + @@ -690,6 +691,7 @@ + diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java index 5a4a2d0ba8d3ea86cb54544f2ef9f988fada00ea..9d97ac58011e12bf34c9dd6fceee03c9719e69df 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java @@ -101,9 +101,10 @@ public class NetworkState { } /* - * Transition from CONNECTED -> DISCONNECTED: - * CONNECTED->DISCONNECTING->DISCONNECTED - * return false if any state transition is not valid and save a message in mReason + * Verifies state transition from CONNECTED->...-> DISCONNECTED. + * + * returns false if initial state or target state is not correct, or if there is + * any transition from DISCONNECTING/DISCONNECTED -> CONNECTED. */ public boolean transitToDisconnection () { mReason = "states: " + printStates(); @@ -120,13 +121,13 @@ public class NetworkState { for (int i = 1; i < mStateDepository.size() - 1; i++) { State preState = mStateDepository.get(i-1); State curState = mStateDepository.get(i); - if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) || + if (preState == curState) { + continue; + } else if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) || (curState == State.DISCONNECTED))) { continue; } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) { continue; - } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) { - continue; } else { mReason += " Transition state from " + preState.toString() + " to " + curState.toString() + " is not valid."; @@ -136,7 +137,12 @@ public class NetworkState { return true; } - // DISCONNECTED->CONNECTING->CONNECTED + /* + * Verifies state transition from DISCONNECTED->...-> CONNECTED. + * + * returns false if initial state or target state is not correct, or if there is + * any transition from CONNECED -> DISCONNECTED. + */ public boolean transitToConnection() { mReason = "states: " + printStates(); if (mStateDepository.get(0) != State.DISCONNECTED) { @@ -152,14 +158,15 @@ public class NetworkState { for (int i = 1; i < mStateDepository.size(); i++) { State preState = mStateDepository.get(i-1); State curState = mStateDepository.get(i); - if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) || - (curState == State.CONNECTED) || (curState == State.DISCONNECTED))) { + if (preState == curState) { continue; - } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) { - continue; - } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) { + } + if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) || + (curState == State.CONNECTED))) { continue; - } else { + } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) { + continue; + } else { mReason += " Transition state from " + preState.toString() + " to " + curState.toString() + " is not valid."; return false; diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl index 4c91ece3b5ef68df63b2b0f3b77dbe3a23d28fba..736b43c80c4d19221ed57226dc159bbb5b11d654 100644 --- a/data/keyboards/AVRCP.kl +++ b/data/keyboards/AVRCP.kl @@ -14,8 +14,8 @@ # Key layout used for Bluetooth AVRCP support. -key 200 MEDIA_PLAY_PAUSE WAKE -key 201 MEDIA_PLAY_PAUSE WAKE +key 200 MEDIA_PLAY WAKE +key 201 MEDIA_PAUSE WAKE key 166 MEDIA_STOP WAKE key 163 MEDIA_NEXT WAKE key 165 MEDIA_PREVIOUS WAKE diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index 695a74f838101ea980c60f8c480e961aa58c632f..01d22ee69178022fc9c86de6809ac6b2aaf41b5b 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -477,4 +477,128 @@ key ESCAPE { ctrl: fallback MENU } -### Gamepad buttons are handled by the view root ### +### Gamepad buttons ### + +key BUTTON_A { + base: fallback DPAD_CENTER +} + +key BUTTON_B { + base: fallback BACK +} + +key BUTTON_C { + base: fallback DPAD_CENTER +} + +key BUTTON_X { + base: fallback DPAD_CENTER +} + +key BUTTON_Y { + base: fallback BACK +} + +key BUTTON_Z { + base: fallback DPAD_CENTER +} + +key BUTTON_L1 { + base: none +} + +key BUTTON_R1 { + base: none +} + +key BUTTON_L2 { + base: none +} + +key BUTTON_R2 { + base: none +} + +key BUTTON_THUMBL { + base: fallback DPAD_CENTER +} + +key BUTTON_THUMBR { + base: fallback DPAD_CENTER +} + +key BUTTON_START { + base: fallback DPAD_CENTER +} + +key BUTTON_SELECT { + base: fallback MENU +} + +key BUTTON_MODE { + base: fallback MENU +} + +key BUTTON_1 { + base: fallback DPAD_CENTER +} + +key BUTTON_2 { + base: fallback DPAD_CENTER +} + +key BUTTON_3 { + base: fallback DPAD_CENTER +} + +key BUTTON_4 { + base: fallback DPAD_CENTER +} + +key BUTTON_5 { + base: fallback DPAD_CENTER +} + +key BUTTON_6 { + base: fallback DPAD_CENTER +} + +key BUTTON_7 { + base: fallback DPAD_CENTER +} + +key BUTTON_8 { + base: fallback DPAD_CENTER +} + +key BUTTON_9 { + base: fallback DPAD_CENTER +} + +key BUTTON_10 { + base: fallback DPAD_CENTER +} + +key BUTTON_11 { + base: fallback DPAD_CENTER +} + +key BUTTON_12 { + base: fallback DPAD_CENTER +} + +key BUTTON_13 { + base: fallback DPAD_CENTER +} + +key BUTTON_14 { + base: fallback DPAD_CENTER +} + +key BUTTON_15 { + base: fallback DPAD_CENTER +} + +key BUTTON_16 { + base: fallback DPAD_CENTER +} diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip deleted file mode 100644 index d3dd02a5e08fba92dfe870b051f9cf965c147290..0000000000000000000000000000000000000000 Binary files a/docs/downloads/training/BitmapFun.zip and /dev/null differ diff --git a/docs/downloads/training/ControllerSample.zip b/docs/downloads/training/ControllerSample.zip new file mode 100644 index 0000000000000000000000000000000000000000..b09686195150e990ffc4206f1be5ac7ed6482cca Binary files /dev/null and b/docs/downloads/training/ControllerSample.zip differ diff --git a/docs/downloads/training/GoogleAuth.zip b/docs/downloads/training/GoogleAuth.zip new file mode 100644 index 0000000000000000000000000000000000000000..18e9bf07207c9fdcb6dc7e2ea918b8f70015dc22 Binary files /dev/null and b/docs/downloads/training/GoogleAuth.zip differ diff --git a/docs/html-intl/es/training/monitoring-device-state/battery-monitoring.jd b/docs/html-intl/intl/es/training/monitoring-device-state/battery-monitoring.jd similarity index 100% rename from docs/html-intl/es/training/monitoring-device-state/battery-monitoring.jd rename to docs/html-intl/intl/es/training/monitoring-device-state/battery-monitoring.jd diff --git a/docs/html-intl/es/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html-intl/intl/es/training/monitoring-device-state/connectivity-monitoring.jd similarity index 100% rename from docs/html-intl/es/training/monitoring-device-state/connectivity-monitoring.jd rename to docs/html-intl/intl/es/training/monitoring-device-state/connectivity-monitoring.jd diff --git a/docs/html-intl/es/training/monitoring-device-state/docking-monitoring.jd b/docs/html-intl/intl/es/training/monitoring-device-state/docking-monitoring.jd similarity index 100% rename from docs/html-intl/es/training/monitoring-device-state/docking-monitoring.jd rename to docs/html-intl/intl/es/training/monitoring-device-state/docking-monitoring.jd diff --git a/docs/html-intl/es/training/monitoring-device-state/index.jd b/docs/html-intl/intl/es/training/monitoring-device-state/index.jd similarity index 100% rename from docs/html-intl/es/training/monitoring-device-state/index.jd rename to docs/html-intl/intl/es/training/monitoring-device-state/index.jd diff --git a/docs/html-intl/es/training/monitoring-device-state/manifest-receivers.jd b/docs/html-intl/intl/es/training/monitoring-device-state/manifest-receivers.jd similarity index 100% rename from docs/html-intl/es/training/monitoring-device-state/manifest-receivers.jd rename to docs/html-intl/intl/es/training/monitoring-device-state/manifest-receivers.jd diff --git a/docs/html-intl/es/training/multiscreen/adaptui.jd b/docs/html-intl/intl/es/training/multiscreen/adaptui.jd similarity index 100% rename from docs/html-intl/es/training/multiscreen/adaptui.jd rename to docs/html-intl/intl/es/training/multiscreen/adaptui.jd diff --git a/docs/html-intl/es/training/multiscreen/index.jd b/docs/html-intl/intl/es/training/multiscreen/index.jd similarity index 100% rename from docs/html-intl/es/training/multiscreen/index.jd rename to docs/html-intl/intl/es/training/multiscreen/index.jd diff --git a/docs/html-intl/es/training/multiscreen/screendensities.jd b/docs/html-intl/intl/es/training/multiscreen/screendensities.jd similarity index 100% rename from docs/html-intl/es/training/multiscreen/screendensities.jd rename to docs/html-intl/intl/es/training/multiscreen/screendensities.jd diff --git a/docs/html-intl/es/training/multiscreen/screensizes.jd b/docs/html-intl/intl/es/training/multiscreen/screensizes.jd similarity index 100% rename from docs/html-intl/es/training/multiscreen/screensizes.jd rename to docs/html-intl/intl/es/training/multiscreen/screensizes.jd diff --git a/docs/html-intl/ja/guide/publishing/app-signing.jd b/docs/html-intl/intl/ja/guide/publishing/app-signing.jd similarity index 100% rename from docs/html-intl/ja/guide/publishing/app-signing.jd rename to docs/html-intl/intl/ja/guide/publishing/app-signing.jd diff --git a/docs/html-intl/ja/guide/publishing/preparing.jd b/docs/html-intl/intl/ja/guide/publishing/preparing.jd similarity index 100% rename from docs/html-intl/ja/guide/publishing/preparing.jd rename to docs/html-intl/intl/ja/guide/publishing/preparing.jd diff --git a/docs/html-intl/ja/guide/publishing/versioning.jd b/docs/html-intl/intl/ja/guide/publishing/versioning.jd similarity index 100% rename from docs/html-intl/ja/guide/publishing/versioning.jd rename to docs/html-intl/intl/ja/guide/publishing/versioning.jd diff --git a/docs/html-intl/ja/guide/topics/fundamentals.jd b/docs/html-intl/intl/ja/guide/topics/fundamentals.jd similarity index 100% rename from docs/html-intl/ja/guide/topics/fundamentals.jd rename to docs/html-intl/intl/ja/guide/topics/fundamentals.jd diff --git a/docs/html-intl/ja/training/monitoring-device-state/battery-monitoring.jd b/docs/html-intl/intl/ja/training/monitoring-device-state/battery-monitoring.jd similarity index 100% rename from docs/html-intl/ja/training/monitoring-device-state/battery-monitoring.jd rename to docs/html-intl/intl/ja/training/monitoring-device-state/battery-monitoring.jd diff --git a/docs/html-intl/ja/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html-intl/intl/ja/training/monitoring-device-state/connectivity-monitoring.jd similarity index 100% rename from docs/html-intl/ja/training/monitoring-device-state/connectivity-monitoring.jd rename to docs/html-intl/intl/ja/training/monitoring-device-state/connectivity-monitoring.jd diff --git a/docs/html-intl/ja/training/monitoring-device-state/docking-monitoring.jd b/docs/html-intl/intl/ja/training/monitoring-device-state/docking-monitoring.jd similarity index 100% rename from docs/html-intl/ja/training/monitoring-device-state/docking-monitoring.jd rename to docs/html-intl/intl/ja/training/monitoring-device-state/docking-monitoring.jd diff --git a/docs/html-intl/ja/training/monitoring-device-state/index.jd b/docs/html-intl/intl/ja/training/monitoring-device-state/index.jd similarity index 100% rename from docs/html-intl/ja/training/monitoring-device-state/index.jd rename to docs/html-intl/intl/ja/training/monitoring-device-state/index.jd diff --git a/docs/html-intl/ja/training/monitoring-device-state/manifest-receivers.jd b/docs/html-intl/intl/ja/training/monitoring-device-state/manifest-receivers.jd similarity index 100% rename from docs/html-intl/ja/training/monitoring-device-state/manifest-receivers.jd rename to docs/html-intl/intl/ja/training/monitoring-device-state/manifest-receivers.jd diff --git a/docs/html-intl/ja/training/multiscreen/adaptui.jd b/docs/html-intl/intl/ja/training/multiscreen/adaptui.jd similarity index 100% rename from docs/html-intl/ja/training/multiscreen/adaptui.jd rename to docs/html-intl/intl/ja/training/multiscreen/adaptui.jd diff --git a/docs/html-intl/ja/training/multiscreen/index.jd b/docs/html-intl/intl/ja/training/multiscreen/index.jd similarity index 100% rename from docs/html-intl/ja/training/multiscreen/index.jd rename to docs/html-intl/intl/ja/training/multiscreen/index.jd diff --git a/docs/html-intl/ja/training/multiscreen/screendensities.jd b/docs/html-intl/intl/ja/training/multiscreen/screendensities.jd similarity index 100% rename from docs/html-intl/ja/training/multiscreen/screendensities.jd rename to docs/html-intl/intl/ja/training/multiscreen/screendensities.jd diff --git a/docs/html-intl/ja/training/multiscreen/screensizes.jd b/docs/html-intl/intl/ja/training/multiscreen/screensizes.jd similarity index 100% rename from docs/html-intl/ja/training/multiscreen/screensizes.jd rename to docs/html-intl/intl/ja/training/multiscreen/screensizes.jd diff --git a/docs/html-intl/ko/training/monitoring-device-state/battery-monitoring.jd b/docs/html-intl/intl/ko/training/monitoring-device-state/battery-monitoring.jd similarity index 100% rename from docs/html-intl/ko/training/monitoring-device-state/battery-monitoring.jd rename to docs/html-intl/intl/ko/training/monitoring-device-state/battery-monitoring.jd diff --git a/docs/html-intl/ko/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html-intl/intl/ko/training/monitoring-device-state/connectivity-monitoring.jd similarity index 100% rename from docs/html-intl/ko/training/monitoring-device-state/connectivity-monitoring.jd rename to docs/html-intl/intl/ko/training/monitoring-device-state/connectivity-monitoring.jd diff --git a/docs/html-intl/ko/training/monitoring-device-state/docking-monitoring.jd b/docs/html-intl/intl/ko/training/monitoring-device-state/docking-monitoring.jd similarity index 100% rename from docs/html-intl/ko/training/monitoring-device-state/docking-monitoring.jd rename to docs/html-intl/intl/ko/training/monitoring-device-state/docking-monitoring.jd diff --git a/docs/html-intl/ko/training/monitoring-device-state/index.jd b/docs/html-intl/intl/ko/training/monitoring-device-state/index.jd similarity index 100% rename from docs/html-intl/ko/training/monitoring-device-state/index.jd rename to docs/html-intl/intl/ko/training/monitoring-device-state/index.jd diff --git a/docs/html-intl/ko/training/monitoring-device-state/manifest-receivers.jd b/docs/html-intl/intl/ko/training/monitoring-device-state/manifest-receivers.jd similarity index 100% rename from docs/html-intl/ko/training/monitoring-device-state/manifest-receivers.jd rename to docs/html-intl/intl/ko/training/monitoring-device-state/manifest-receivers.jd diff --git a/docs/html-intl/ko/training/multiscreen/adaptui.jd b/docs/html-intl/intl/ko/training/multiscreen/adaptui.jd similarity index 100% rename from docs/html-intl/ko/training/multiscreen/adaptui.jd rename to docs/html-intl/intl/ko/training/multiscreen/adaptui.jd diff --git a/docs/html-intl/ko/training/multiscreen/index.jd b/docs/html-intl/intl/ko/training/multiscreen/index.jd similarity index 100% rename from docs/html-intl/ko/training/multiscreen/index.jd rename to docs/html-intl/intl/ko/training/multiscreen/index.jd diff --git a/docs/html-intl/ko/training/multiscreen/screendensities.jd b/docs/html-intl/intl/ko/training/multiscreen/screendensities.jd similarity index 100% rename from docs/html-intl/ko/training/multiscreen/screendensities.jd rename to docs/html-intl/intl/ko/training/multiscreen/screendensities.jd diff --git a/docs/html-intl/ko/training/multiscreen/screensizes.jd b/docs/html-intl/intl/ko/training/multiscreen/screensizes.jd similarity index 100% rename from docs/html-intl/ko/training/multiscreen/screensizes.jd rename to docs/html-intl/intl/ko/training/multiscreen/screensizes.jd diff --git a/docs/html-intl/ru/training/monitoring-device-state/battery-monitoring.jd b/docs/html-intl/intl/ru/training/monitoring-device-state/battery-monitoring.jd similarity index 100% rename from docs/html-intl/ru/training/monitoring-device-state/battery-monitoring.jd rename to docs/html-intl/intl/ru/training/monitoring-device-state/battery-monitoring.jd diff --git a/docs/html-intl/ru/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html-intl/intl/ru/training/monitoring-device-state/connectivity-monitoring.jd similarity index 100% rename from docs/html-intl/ru/training/monitoring-device-state/connectivity-monitoring.jd rename to docs/html-intl/intl/ru/training/monitoring-device-state/connectivity-monitoring.jd diff --git a/docs/html-intl/ru/training/monitoring-device-state/docking-monitoring.jd b/docs/html-intl/intl/ru/training/monitoring-device-state/docking-monitoring.jd similarity index 100% rename from docs/html-intl/ru/training/monitoring-device-state/docking-monitoring.jd rename to docs/html-intl/intl/ru/training/monitoring-device-state/docking-monitoring.jd diff --git a/docs/html-intl/ru/training/monitoring-device-state/index.jd b/docs/html-intl/intl/ru/training/monitoring-device-state/index.jd similarity index 100% rename from docs/html-intl/ru/training/monitoring-device-state/index.jd rename to docs/html-intl/intl/ru/training/monitoring-device-state/index.jd diff --git a/docs/html-intl/ru/training/monitoring-device-state/manifest-receivers.jd b/docs/html-intl/intl/ru/training/monitoring-device-state/manifest-receivers.jd similarity index 100% rename from docs/html-intl/ru/training/monitoring-device-state/manifest-receivers.jd rename to docs/html-intl/intl/ru/training/monitoring-device-state/manifest-receivers.jd diff --git a/docs/html-intl/ru/training/multiscreen/adaptui.jd b/docs/html-intl/intl/ru/training/multiscreen/adaptui.jd similarity index 100% rename from docs/html-intl/ru/training/multiscreen/adaptui.jd rename to docs/html-intl/intl/ru/training/multiscreen/adaptui.jd diff --git a/docs/html-intl/ru/training/multiscreen/index.jd b/docs/html-intl/intl/ru/training/multiscreen/index.jd similarity index 100% rename from docs/html-intl/ru/training/multiscreen/index.jd rename to docs/html-intl/intl/ru/training/multiscreen/index.jd diff --git a/docs/html-intl/ru/training/multiscreen/screendensities.jd b/docs/html-intl/intl/ru/training/multiscreen/screendensities.jd similarity index 100% rename from docs/html-intl/ru/training/multiscreen/screendensities.jd rename to docs/html-intl/intl/ru/training/multiscreen/screendensities.jd diff --git a/docs/html-intl/ru/training/multiscreen/screensizes.jd b/docs/html-intl/intl/ru/training/multiscreen/screensizes.jd similarity index 100% rename from docs/html-intl/ru/training/multiscreen/screensizes.jd rename to docs/html-intl/intl/ru/training/multiscreen/screensizes.jd diff --git a/docs/html-intl/zh-cn/training/monitoring-device-state/battery-monitoring.jd b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/battery-monitoring.jd similarity index 100% rename from docs/html-intl/zh-cn/training/monitoring-device-state/battery-monitoring.jd rename to docs/html-intl/intl/zh-cn/training/monitoring-device-state/battery-monitoring.jd diff --git a/docs/html-intl/zh-cn/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/connectivity-monitoring.jd similarity index 100% rename from docs/html-intl/zh-cn/training/monitoring-device-state/connectivity-monitoring.jd rename to docs/html-intl/intl/zh-cn/training/monitoring-device-state/connectivity-monitoring.jd diff --git a/docs/html-intl/zh-cn/training/monitoring-device-state/docking-monitoring.jd b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/docking-monitoring.jd similarity index 100% rename from docs/html-intl/zh-cn/training/monitoring-device-state/docking-monitoring.jd rename to docs/html-intl/intl/zh-cn/training/monitoring-device-state/docking-monitoring.jd diff --git a/docs/html-intl/zh-cn/training/monitoring-device-state/index.jd b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/index.jd similarity index 100% rename from docs/html-intl/zh-cn/training/monitoring-device-state/index.jd rename to docs/html-intl/intl/zh-cn/training/monitoring-device-state/index.jd diff --git a/docs/html-intl/zh-cn/training/monitoring-device-state/manifest-receivers.jd b/docs/html-intl/intl/zh-cn/training/monitoring-device-state/manifest-receivers.jd similarity index 100% rename from docs/html-intl/zh-cn/training/monitoring-device-state/manifest-receivers.jd rename to docs/html-intl/intl/zh-cn/training/monitoring-device-state/manifest-receivers.jd diff --git a/docs/html-intl/zh-cn/training/multiscreen/adaptui.jd b/docs/html-intl/intl/zh-cn/training/multiscreen/adaptui.jd similarity index 100% rename from docs/html-intl/zh-cn/training/multiscreen/adaptui.jd rename to docs/html-intl/intl/zh-cn/training/multiscreen/adaptui.jd diff --git a/docs/html-intl/zh-cn/training/multiscreen/index.jd b/docs/html-intl/intl/zh-cn/training/multiscreen/index.jd similarity index 100% rename from docs/html-intl/zh-cn/training/multiscreen/index.jd rename to docs/html-intl/intl/zh-cn/training/multiscreen/index.jd diff --git a/docs/html-intl/zh-cn/training/multiscreen/screendensities.jd b/docs/html-intl/intl/zh-cn/training/multiscreen/screendensities.jd similarity index 100% rename from docs/html-intl/zh-cn/training/multiscreen/screendensities.jd rename to docs/html-intl/intl/zh-cn/training/multiscreen/screendensities.jd diff --git a/docs/html-intl/zh-cn/training/multiscreen/screensizes.jd b/docs/html-intl/intl/zh-cn/training/multiscreen/screensizes.jd similarity index 100% rename from docs/html-intl/zh-cn/training/multiscreen/screensizes.jd rename to docs/html-intl/intl/zh-cn/training/multiscreen/screensizes.jd diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index 5f0779c1f3f759011a0817e5071f6708b21b7246..fc60e1ff01506c78f2dd6521ae41d0d43953b1c9 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -313,6 +313,9 @@ redirects: # -------------------- MISC ---------------------- +- from: /shareables/training/BitmapFun.zip + to: /downloads/samples/DisplayingBitmaps.zip + - from: /shareables/... to: http://commondatastorage.googleapis.com/androiddevelopers/shareables/... diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd index bb34495992ec33bf197d27a1eaf737d7a5a5fcdb..6d29c69cd12539d96dcdde9afd6774aea0e8c26c 100644 --- a/docs/html/about/dashboards/index.jd +++ b/docs/html/about/dashboards/index.jd @@ -61,7 +61,7 @@ Platform Versions.

-

Data collected during a 7-day period ending on November 1, 2013. +

Data collected during a 7-day period ending on March 3, 2014.
Any versions with less than 0.1% distribution are not shown.

@@ -92,7 +92,7 @@ Screens.

-

Data collected during a 7-day period ending on November 1, 2013 +

Data collected during a 7-day period ending on March 3, 2014.
Any screen configurations with less than 0.1% distribution are not shown.

@@ -111,7 +111,7 @@ support for any lower version (for example, support for version 2.0 also implies +src="//chart.googleapis.com/chart?chl=GL%201.1%20only%7CGL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A0.1%2C93.5%2C6.4&chco=c4df9b%2C6fad0c&chs=400x250&cht=p" />

To declare which version of OpenGL ES your application requires, you should use the {@code android:glEsVersion} attribute of the 2.0 -98.3% +91.1% 3.0 -1.6% +8.8% -

Data collected during a 7-day period ending on November 1, 2013

+

Data collected during a 7-day period ending on March 3, 2014

@@ -161,17 +161,17 @@ uses.

var VERSION_DATA = [ { - "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CHoneycomb%7CIce%20Cream%20Sandwich%7CJelly%20Bean&chco=c4df9b%2C6fad0c&chd=t%3A1.7%2C26.3%2C0.1%2C19.8%2C52.1&chf=bg%2Cs%2C00000000&chs=500x250&cht=p", + "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CHoneycomb%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chd=t%3A1.2%2C19.0%2C0.1%2C15.2%2C62.0%2C2.5&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&chs=500x250&cht=p", "data": [ { "api": 8, "name": "Froyo", - "perc": "1.7" + "perc": "1.2" }, { "api": 10, "name": "Gingerbread", - "perc": "26.3" + "perc": "19.0" }, { "api": 13, @@ -181,22 +181,27 @@ var VERSION_DATA = { "api": 15, "name": "Ice Cream Sandwich", - "perc": "19.8" + "perc": "15.2" }, { "api": 16, "name": "Jelly Bean", - "perc": "37.3" + "perc": "35.3" }, { "api": 17, "name": "Jelly Bean", - "perc": "12.5" + "perc": "17.1" }, { "api": 18, "name": "Jelly Bean", - "perc": "2.3" + "perc": "9.6" + }, + { + "api": 19, + "name": "KitKat", + "perc": "2.5" } ] } @@ -211,30 +216,31 @@ var SCREEN_DATA = { "data": { "Large": { - "hdpi": "0.5", - "ldpi": "0.6", - "mdpi": "3.6", - "tvdpi": "1.2", - "xhdpi": "0.5" + "hdpi": "0.6", + "ldpi": "0.7", + "mdpi": "4.3", + "tvdpi": "1.5", + "xhdpi": "0.6" }, "Normal": { - "hdpi": "33.4", - "ldpi": "0.1", - "mdpi": "15.1", - "xhdpi": "22.2", - "xxhdpi": "8.8" + "hdpi": "33.7", + "ldpi": "0.2", + "mdpi": "13.6", + "xhdpi": "19.9", + "xxhdpi": "11.9" }, "Small": { - "ldpi": "9.2" + "ldpi": "8.1" }, "Xlarge": { "hdpi": "0.3", - "mdpi": "4.4", - "xhdpi": "0.1" + "ldpi": "0.1", + "mdpi": "4.3", + "xhdpi": "0.2" } }, - "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chco=c4df9b%2C6fad0c&chd=t%3A9.9%2C23.1%2C1.2%2C34.2%2C22.8%2C8.8&chf=bg%2Cs%2C00000000&chs=400x250&cht=p", - "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chco=c4df9b%2C6fad0c&chd=t%3A4.8%2C6.4%2C79.6%2C9.2&chf=bg%2Cs%2C00000000&chs=400x250&cht=p" + "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chd=t%3A9.1%2C22.2%2C1.5%2C34.6%2C20.7%2C11.9&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&chs=400x250&cht=p", + "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.9%2C7.7%2C79.3%2C8.1&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&chs=400x250&cht=p" } ]; @@ -301,6 +307,11 @@ var VERSION_NAMES = "api":18, "link":"
4.3", "codename":"Jelly Bean" + }, + { + "api":19, + "link":"4.4", + "codename":"KitKat" } ]; diff --git a/docs/html/about/versions/android-4.4.jd b/docs/html/about/versions/android-4.4.jd index 42f257c01f3a90dcf6076b19a67ca3acad4df189..3de2acca7049e3682a1a70e4012473a95509575d 100644 --- a/docs/html/about/versions/android-4.4.jd +++ b/docs/html/about/versions/android-4.4.jd @@ -244,7 +244,7 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targe

If you want to emulate an NFC card that is using these protocols in your app, create a service component based on the {@link android.nfc.cardemulation.HostApduService} class. Whereas if your app instead uses a secure element for card emulation, you need to create a service based on the {@link android.nfc.cardemulation.OffHostApduService} class, which will not directly be involved in the transactions but is necessary to register the AIDs that should be handled by the secure element.

-

For more information, read the NFC Card Emulation guide.

+

For more information, read the NFC Card Emulation guide.

NFC reader mode

diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd index c6702b075bf3b6adf42cc3d521c1dcd66847d683..25f88e3eb2b56c50b9d30bf25297dc2170f46588 100644 --- a/docs/html/about/versions/jelly-bean.jd +++ b/docs/html/about/versions/jelly-bean.jd @@ -578,9 +578,10 @@ graphs, with colors indicating time spent creating drawing commands graph for each visible Activity, including the navigation bar and notification bar.

-

A green line highlights the 60ms threshold for rendering +

A green line highlights the 16ms threshold for rendering operations, so you can assess the your app’s effective framerate relative -to a 60 fps goal. If you see operations that cross the green line, you +to a 60 fps goal (because 1/60th of a second equals roughly 16ms). +If you see operations that cross the green line, you can analyze them further using Systrace and other tools.

On devices running Android 4.2 and higher, diff --git a/docs/html/about/versions/kitkat.jd b/docs/html/about/versions/kitkat.jd index 5e442ec61b37cce532a58ec90a72e31eae78e659..4237c9868a091e96643e0d8772ece27d44c7f695 100644 --- a/docs/html/about/versions/kitkat.jd +++ b/docs/html/about/versions/kitkat.jd @@ -54,8 +54,8 @@ window.onhashchange = function () {

-
- Android 4.4 on phone and tablet +
+ Android 4.4 on phone and tablet
diff --git a/docs/html/channels/io2013.jd b/docs/html/channels/io2013.jd index 977eb2f2aed34ede98634f4964cb3c02373db812..2efda28d2b8c676f5ae8e9e96bb2f3d058b37703 100644 --- a/docs/html/channels/io2013.jd +++ b/docs/html/channels/io2013.jd @@ -1,3 +1,4 @@ +excludeFromSuggestions=true fullpage=true page.title=Google I/O 13 @jd:body diff --git a/docs/html/community/index.html b/docs/html/community/index.html new file mode 100644 index 0000000000000000000000000000000000000000..eeb1c5144c5033bd73e134511f448dc43f44757b --- /dev/null +++ b/docs/html/community/index.html @@ -0,0 +1,317 @@ + + + + + + + + +Android Developers Community + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

+ Android is working with communities to +

    +
  • Create a cooperative relationship between highly impactful communities and high performing developers
  • +
  • Strengthen relationships between Android and coding communities so that developers have a motivation to interact with those communities
  • +
  • Reward communities for attracting developers by providing them with resources and direct access to the Android team
+

Our IMPACT is measured by good apps and a thriving community

+ +

News

+

Our current theme is on the Android 4.4 KitKat and Updated Developer Tools release, with new ways to create beautiful apps, printing and storage frameworks, low-power sensors, new media capabilities and RenderScript in the NDK.

+ +
+ +
+ +
+

Android 4.4 Platform Highlights

+

Android KitKat brings all of Android's most innovative, most beautiful, and most useful features to more devices everywhere.

+ +

Android 4.4 APIs

+

Android 4.4 (KITKAT) is a new release for the Android platform that offers new features for users and app developers. This document provides an introduction to the most notable new APIs.

+ +

New Nexus 5

+

Nexus 5 helps you capture the everyday and the epic in fresh new ways. It's the slimmest and fastest Nexus phone ever made, powered by Android 4.4, KitKat.

+
+

+ +

Community Spotlight

+ +
+

Android Community Groups

+

July 2013

+

GDG Armenia held ecaHack Yerevan, an Android hackathon featuring "Angry Designers vs Android Developers", where the designers had a look into developers apps and gave them some advices regarding to design. The hackathon was sponsored by Alcatel One Touch and each member of the winner team got one Alcatel One Touch Idol. Check out the photos.

+

September 2013

+

GDG Blrdroid held a meetup on performance optimisation.

+

GDG Dublin held their meetup with a talk on AdMob for Android and iOS, entitled “Admob for Android and iOS developers”.

+

GDG Mbale’s Hassan Nsubuga has been managing a university course session since September. They are now through with the basics and have a firm foundation, so this month they will be doing a lot of advanced stuff including exploring the latest API enhancements with KitKat.

+

GDG Warsaw held an Android Barcamp focused on App Quality. The discussion was moderated by GDG organisers, but the talks were given by the community members, including some top Android companies and developers in Poland.

+

October 2013

+

GDG Berlin Android held their DevFest.

+

GDG Blrdroid held their meetup in collaboration with GDG Bangalore, where they talked about a wider range of things from incorporating user feedback to effectively using the cell radio. David McLaughlin from the Developer Relations team also visited during the meetup and delivered the keynote. They also hit a milestone with its 4th anniversary on the 9th of October and crossed 4300 members in the past few days so its been a memorable couple of months for them.

+

GDG Cape Town held an Android Workshop where they gave away lots of branded KitKats.

+

GDG Dublin held its DevFest, which featured a codeLab on Android titled “Codelab: Intro to Android Development.”

+

GDG Dutch Android User Group held their DevFest. They had a bunch of Android sessions, mostly by local speakers. In addition to the Android sessions, they also ran a workshop on writing custom views.

+

Hugo Visser from the Dutch Android User Group spoke at DroidCon UK barcamp, where he delivered a talk on Cupboard, a simple and lightweight persistence framework, specifically for Android.

+

GDG GAUG held the Google Devfest 2013, where they had two tracks and more than 200 delegates attending. They also had a Hackathon and they hit the 1000 member mark this month, which makes them the second largest android community in India after GDG Blrdroid.

+

GDG Miage held their DevFest where they gave a talk about Intents and Services. The also held a startup Weekend Bootcamp where they talked about Activites and Layouts. They will also hold an Android Workshop in December.

+

GDG Uruguay had their DevFest, where they held an Android workshop for beginners as an Android vs iOS comparison, a session on best practices using YouTube APIs in Android, and  What's new in Google for developers (with a special section about Android). You can see pictures on the G+ page.

+ +

November 2013

+

Abidjandroid/GDG Côte d'Ivoire held an Android Launch Party featuring the KitKat release.

+

The Dutch Android User Group had a very interactive presentation on Android Code Puzzlers and Tips & tricks, where they rewarded participation by giving out books, tshirts, jelly beans and kitkats. The presentation was at the Dutch JFall conference, organized by the NLJUG. It's a large yearly Java conference and the DAUG had the only Android session there.

+

The GDG Berlin Android meetup this month featured the KitKat release.

+

The GDG Blrdroid meetup was another focused on KitKat.

+

At the GDG Cairo DevFest there was a "What's new in Android SDK" session on day 1, and an Android workshop on day 2. Kitkat also provided interest in the sessions and the snacks bar. The KitKat presentation, the track organization, and everything related to it were all organized by women.

+

GDG Cape Town held an Android Workshop.

+

GDG Udine organized a talk after the release of KitKat for a school in Pordenone.

+

Hugo Visser from Droidcon Netherlands organized an Android hackathon themed "Location, Location, Location".          

+

Paris Android User Group welcomed Romain Guy and Chet Haase to their meetup this month. They’ll be meeting up with other GDG leads and UG managers meet at Devoxx next Thursday.

+

GDG Warsaw had over 250 attendees at their DevFest, which featured session tracks on Android and Web and a whole day of Code Labs in Android, AngularJS and Arduino.

+

December 2013

+

GDG GAUG are planning a codelab and hackathon.

+

Peter Svensson spoke at DroidCon Stockholm

+The unstoppable Max Bonbhel from the African GDG Android is hosting AAC 2014 and Android GDG Barcamp events in December. Also, in order to encourage African Java developers to move to the Android platform, he created the Africa Android Training (AAT) program. The training material targets developers with different levels of experience in Java development. More than 60 developers have been taking part in the weekly sessions. The next 10 sessions will start Saturday, November 9, 2013. 260 GDG and Java User Group members have already registered from 12 Countries. +

 

+
+ +
+

Android Community Experts

+ +

October 2013

+

Eyal Lezmy presented two sessions. “Play Store bashing, learn from the biggest fails” looked at several applications, mainly developed by huge companies, and analyzed why they failed to satisfy the users or the Android guidelines. “Android, the life of your app” tells a story, living the life of a user, identify the frustrations he can encounter and present ways to avoid it, as a developer.

+

Mario Viviani presented and recorded the next Android App Clinic - Italia. This episode was regarding the Cards UI and SMS app support in Android 4.4. They experimented with a short form of the video (10 minutes instead of 20) and in less than day it got almost 400+ views -- which is great considering it's in Italian! The previous episode reached 1300 views all-time and was the most successful video of GDL Italia in Q2.

+

Matthias Käppler contributed the first Android specific component to the RxJava project, and spoke about RxJava and reactive programming on Android at DroidCon UK. He has also open sourced Memento, an Android annotation processor to replace the deprecated onRetainNonConfigurationInstance.

+

Wojtek Kaliciński’s talk, "Android - is it time for a break yet?" highlights not only what's new in Android 4.4 KitKat, but also how to take your app to the next level by making sure you provide the best app experience possible to all 4.0+ users.

+ + +
+ +

New Content

+
+

Android 4.4 What's New

+KitKat has been optimized to run on a much broader range of devices, with special focus on the millions of entry-level devices that have as little as 512MB RAM. To help, we've created new APIs, better tools, and better documentation to let you create apps that perform well on all devices.
+Check out this video summary of some of the most significant developer features in the latest Android release, including new ways to make your apps beautiful, NFC Host Card Emulation, a printing framework, the storage access framework, low-power step detector and step counter sensors, and more!
+
Video
+
Presentation
+Be sure to get the full Android 4.4 API Overview, and take a look at our related DevBytes videos

+ +

WebView in Android 4.4

+Android 4.4 (API level 19) introduces a new version of WebView that is based on Chromium. This change upgrades WebView performance and standards support for HTML5, CSS3, and JavaScript to match the latest web browsers. Any apps using WebView will inherit these upgrades when running on Android 4.4 and higher. +
API Guide
+

+ +

Android 4.4 Immersive Mode

+With Android 4.4 KitKat, your apps can now truly go full-screen with a new Immersive Mode. Immersive Mode lets your apps hide the system's status and navigation bars while capturing all touch events—ideal for rich interactive content such as books and games. This video demonstrates how to use the new API, in addition to recapping earlier full-screen APIs on Android. +
Video
+
Presentation
+
Sample
+

+ +

+

Android 4.4 Storage Access Framework - Provider

+Get up to speed on the new document storage API in Android 4.4 KitKat. This video gets you up and running with your own DocumentProvider by stepping you through the making of a simple cloud storage app. +
Video
+
Presentation
+
Training
+
Sample, Box Application
+ + +

+ + +

Android 4.4 Storage Access Framework - Client

+Get up to speed on the new storage access framework in Android 4.4 KitKat. This video teaches you how to quickly create, edit, save and delete documents provided by other apps as a client of the storage access framework. +
Video
+
Presentation
+
Sample
+

+ +

Android 4.4 Closed Captioning

+Displaying closed captions in your app's videos can be quick and simple in Android 4.4 KitKat,. Learn how to attach timed text tracks to VideoView and allow users to customize how captions are displayed. +
Video
+
Presentation +

+
+
+ +
+ +

+

Android 4.4 Transitions

+In this episode, we introduce the new Transitions API in Android 4.4 Kitkat. This API provides a simple way for developers to provide animated segues to different scenes of their application, helping users to understand the application flow with very little code. The general approach is to tell the system that you'd like to run a transition animation, then make the necessary changes to your UI. The system figures out the differences and animates the changes. +
Video
+
Presentation

+
+

Android 4.4: SMS APIs

+Android 4.4 KitKat introduces the new SMS APIs as well as the new concept of a default SMS app. This video discusses these new APIs and how your app should use them to send and receive SMS and MMS messages.
+
Video
+
Presentation
+See also -
+Android Developer Blog post on Android 4.4 KitKat SMS APIs
+Android Protip on using booleans in your AndroidManifest.xml

+ + +

Android 4.4 Printing API

+In this video, we introduce the new Printing API in Android 4.4 KitKat. This API provides a simple way for developers to print to cloud-connected printers using Google Cloud Print. It's really easy to print bitmaps, and HTML (that you generate on the device, or just web content).
+
Video
+
Presentation
+
Training
+Some pro-tips: +
    +
  • For Webview/HTML printing, printing from javascript is not supported yet (window.print() for example). Also we are planning to address the limitations around WebView/HTML printing in future releases (eg: headers/footers, and specifying print ranges).
  • +
  • We encourage developers to open Android Open Source bugs for features that they feel important as a feedback.
  • +
+ +

App Indexing

+In this episode we discuss the new App Indexing feature that we recently announced for Google Search for Android.
+Currently, when you do a google search on the web, you get results that are links to websites. With App Indexing, you will be able to point Google Search users on Android directly to content within your app! +If you’re an Android app developer who has a web presence and you want more control over how your content is accessed from search, via your website or Android app, App Indexing is a great feature for you to explore. +Also, enabling your website and app for indexing is a way to increase engagement with your app by making the content more discoverable, and more accessible to users directly from the search results page. +For information on App Indexing, please visit http://g.co/appindexing +
Video
+
Presentation +

+
+
+ +

Social

+ + + + +
+ + + + + diff --git a/docs/html/design/building-blocks/buttons.jd b/docs/html/design/building-blocks/buttons.jd index 1f7e25d35b0dda48935a8983684e392f64083d0a..8a65b825584f8e5c49bab8fa744b81a29f00d7ac 100644 --- a/docs/html/design/building-blocks/buttons.jd +++ b/docs/html/design/building-blocks/buttons.jd @@ -9,7 +9,7 @@ page.tags="button","input"
-

A button consists of text and/or an image that clearly communicates what action +

A button consists of text and/or an image that clearly communicates what action will occur when the user touches it. A button can have an image, text, or both.

diff --git a/docs/html/design/building-blocks/dialogs.jd b/docs/html/design/building-blocks/dialogs.jd index f4bb87e9303c4100f1fd0a4aa65a746764a62674..7cd032cdc29e3d1d2ca476635400444399e8734c 100644 --- a/docs/html/design/building-blocks/dialogs.jd +++ b/docs/html/design/building-blocks/dialogs.jd @@ -9,7 +9,7 @@ page.tags="dialog","alert","popup","toast"
-

Dialogs prompt the user for decisions or additional information required by the app to continue a +

Dialogs prompt the user for decisions or additional information required by the app to continue a task. Such requests can range from simple Cancel/OK decisions to more complex layouts asking the user to adjust settings or enter text.

diff --git a/docs/html/design/building-blocks/grid-lists.jd b/docs/html/design/building-blocks/grid-lists.jd index 1a09ef551082aa294cbd3ac922aab5dc4a3e0ca7..6c9d2278b783c349b3d1a14da416dc746bb49442 100644 --- a/docs/html/design/building-blocks/grid-lists.jd +++ b/docs/html/design/building-blocks/grid-lists.jd @@ -11,7 +11,7 @@ page.tags="gridview","layout","listview"
-

Grid lists are an alternative to standard list views. They are best suited for showing data sets +

Grid lists are an alternative to standard list views. They are best suited for showing data sets that represent themselves through images. In contrast to simple lists, grid lists may scroll either vertically or horizontally.

diff --git a/docs/html/design/building-blocks/index.jd b/docs/html/design/building-blocks/index.jd index e554775679156cf0fcf73eb2b1ee3262232a5edb..7fb0e553be37b62199bfdc0b7adf3ded168d006b 100644 --- a/docs/html/design/building-blocks/index.jd +++ b/docs/html/design/building-blocks/index.jd @@ -18,7 +18,8 @@ footer.hide=1
- Your inventory of ready-to-use elements for creating outstanding apps. + Your inventory of ready-to-use elements for creating + outstanding apps.

Tabs
diff --git a/docs/html/design/building-blocks/lists.jd b/docs/html/design/building-blocks/lists.jd index 5514824b056b35f7e049a19b98037f528eb4f554..0bbd08bb50048ad0a6cb66f879e617569637b992 100644 --- a/docs/html/design/building-blocks/lists.jd +++ b/docs/html/design/building-blocks/lists.jd @@ -9,7 +9,7 @@ page.tags="listview","layout"
-

Lists present multiple line items in a vertical arrangement. They can be used for data selection as +

Lists present multiple line items in a vertical arrangement. They can be used for data selection as well as drilldown navigation.

 
diff --git a/docs/html/design/building-blocks/pickers.jd b/docs/html/design/building-blocks/pickers.jd index 6dd72ba06c22ca4f751fd0c60be423ba4127cf7d..9473d1106429aa239c8e2ba7026c7a661f375ecd 100644 --- a/docs/html/design/building-blocks/pickers.jd +++ b/docs/html/design/building-blocks/pickers.jd @@ -9,7 +9,7 @@ page.tags="datepicker","timepicker" -

Pickers provide a simple way to select a single value from a set. In addition to touching the +

Pickers provide a simple way to select a single value from a set. In addition to touching the up/down arrow buttons, it's possible to set the desired value from the keyboard or via a swipe gesture.

diff --git a/docs/html/design/building-blocks/spinners.jd b/docs/html/design/building-blocks/spinners.jd index c00b639b39fb7f565a5d2524778b9220e64440c0..cdc933b6d6094f8b02307a41538514bb1a63064e 100644 --- a/docs/html/design/building-blocks/spinners.jd +++ b/docs/html/design/building-blocks/spinners.jd @@ -9,7 +9,7 @@ page.tags="spinner","dropdown" -

Spinners provide a quick way to select one value from a set. In the default state, a spinner shows +

Spinners provide a quick way to select one value from a set. In the default state, a spinner shows its currently selected value. Touching the spinner displays a dropdown menu with all other available values, from which the user can select a new one.

diff --git a/docs/html/design/building-blocks/switches.jd b/docs/html/design/building-blocks/switches.jd index 74cab5ae21012cfd3c4786b508124bb2b8cf4c52..dff6c73d5841f4ecd20a938b982b819d92a65329 100644 --- a/docs/html/design/building-blocks/switches.jd +++ b/docs/html/design/building-blocks/switches.jd @@ -16,7 +16,7 @@ buttons, and on/off switches.

-

Checkboxes allow the user to select multiple options from a set. Avoid using a single checkbox to +

Checkboxes allow the user to select multiple options from a set. Avoid using a single checkbox to turn an option off or on. Instead, use an on/off switch.

diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd index 5a5da5d8ad9706885eaeab79a24e197a49ee9f25..2186b181bc22b058c3143c0566cf4035bbce4074 100644 --- a/docs/html/design/building-blocks/tabs.jd +++ b/docs/html/design/building-blocks/tabs.jd @@ -11,7 +11,7 @@ page.tags="tabs","actionbar","navigation","viewpager" -

Tabs in the action bar make it easy to explore and switch between different views or functional +

Tabs in the action bar make it easy to explore and switch between different views or functional aspects of your app, or to browse categorized data sets.

For details on using gestures to move between tabs, see the Swipe Views pattern.

diff --git a/docs/html/design/building-blocks/text-fields.jd b/docs/html/design/building-blocks/text-fields.jd index 383531b1adadf63e7cd605e5240af48da8bfe7cf..3840ca2808e27971c40949b08c46efa153e01ee2 100644 --- a/docs/html/design/building-blocks/text-fields.jd +++ b/docs/html/design/building-blocks/text-fields.jd @@ -9,7 +9,7 @@ page.tags="text","edittext","input" -

Text fields allow the user to type text into your app. They can be either single line or multi-line. +

Text fields allow the user to type text into your app. They can be either single line or multi-line. Touching a text field places the cursor and automatically displays the keyboard. In addition to typing, text fields allow for a variety of other activities, such as text selection (cut, copy, paste) and data lookup via auto-completion.

diff --git a/docs/html/design/get-started/creative-vision.jd b/docs/html/design/get-started/creative-vision.jd index c57b185c701b94ea4f3cd43e4d78cafeafc48014..1ce305a5a64d0406832235266350778885e515c9 100644 --- a/docs/html/design/get-started/creative-vision.jd +++ b/docs/html/design/get-started/creative-vision.jd @@ -5,7 +5,12 @@ page.title=Creative Vision
 
-

We focused the design of Android around three overarching goals, which apply to our core apps as well as the system at large. As you design apps to work with Android, consider these goals:

+

+ We focused the design of Android around three overarching goals, which apply + to our core apps as well as the system at large. As you design apps to work + with Android, consider these goals: Enchant me, Simplify my + life, and Make me amazing +

 
diff --git a/docs/html/design/index.jd b/docs/html/design/index.jd index 8f73d9ce0e8530d26f8dfe5ee2dc68220953d417..9ba32dd5236b69451aa1a03261eb18a8c2f3cedf 100644 --- a/docs/html/design/index.jd +++ b/docs/html/design/index.jd @@ -22,7 +22,8 @@ footer.hide=1
- Welcome to Android Design, your place for learning how to design exceptional Android apps. + Welcome to Android Design, your place for + learning how to design exceptional Android apps.

Want to know what Android 4.4 KitKat has for designers? See New in Android.

Creative Vision diff --git a/docs/html/design/media/progress_download.png b/docs/html/design/media/progress_download.png index 5c9e556569ecc5c55f80b3ad559bf1138ef45490..576509539502e85b3f7670d54934b013e6e9cec6 100644 Binary files a/docs/html/design/media/progress_download.png and b/docs/html/design/media/progress_download.png differ diff --git a/docs/html/design/patterns/accessibility.jd b/docs/html/design/patterns/accessibility.jd index 532900ecd80c54b07563e8c573200456dbd7a88e..aaa6f163ca2af130032a1db2fd33c97d3198637e 100644 --- a/docs/html/design/patterns/accessibility.jd +++ b/docs/html/design/patterns/accessibility.jd @@ -1,5 +1,6 @@ page.title=Accessibility page.tags="accessibility","navigation","input" +page.metaDescription=Design an app that's universally accessible to people with visual impairment, color deficiency, hearing loss, and limited dexterity. @jd:body diff --git a/docs/html/design/patterns/actionbar.jd b/docs/html/design/patterns/actionbar.jd index b6e3a16563d269f1f770f167593445e67282fa50..a1adbd33b4a63c169e9fe5c355646c3121c32d0d 100644 --- a/docs/html/design/patterns/actionbar.jd +++ b/docs/html/design/patterns/actionbar.jd @@ -1,5 +1,6 @@ page.title=Action Bar page.tags="actionbar","navigation" +page.metaDescription=The Action bar is an essential design element for all apps. Learn about what the action bar can do and how to use it in your apps. @jd:body diff --git a/docs/html/design/patterns/compatibility.jd b/docs/html/design/patterns/compatibility.jd index 5ca6d8b9e7929d6605f3eb9f4506be471e2e80ca..dfc52c0270fb2f75de884adebae6a7f4bc16cbbc 100644 --- a/docs/html/design/patterns/compatibility.jd +++ b/docs/html/design/patterns/compatibility.jd @@ -1,5 +1,6 @@ page.title=Backwards Compatibility page.tags="support" +page.metaDescription=Notes on how Android 4.x adapts UI designed for older hardware and OS versions. @jd:body diff --git a/docs/html/design/patterns/help.jd b/docs/html/design/patterns/help.jd index ad5742d78757fd1fa8ac7393c69c6b3e25a2ed52..e47bc5a71bad023aeaa2deeaa6b02382de892fb2 100644 --- a/docs/html/design/patterns/help.jd +++ b/docs/html/design/patterns/help.jd @@ -6,7 +6,7 @@ page.tags="settings","preferences"

Some of your users will run into questions or problems along the way. They'll be looking for answers within your app, and if they don't find them quickly, they may leave and never come back.

-

This page covers design patterns for making help accessible in your app and tips for creating help content for users who are eager for assistance.

+

This page covers design patterns for making help accessible in your app and tips for creating help content for users who are eager for assistance.

Designing Help into Your App

diff --git a/docs/html/design/patterns/index.jd b/docs/html/design/patterns/index.jd index 4416de1daa5badab80e57a572001a374b97d0367..e091a29ca5e8effa64403355fc3f25cd1e181a0a 100644 --- a/docs/html/design/patterns/index.jd +++ b/docs/html/design/patterns/index.jd @@ -18,7 +18,8 @@ footer.hide=1
diff --git a/docs/html/design/patterns/multi-pane-layouts.jd b/docs/html/design/patterns/multi-pane-layouts.jd index 06c81897a6b559bd627a2543e8737b954f226c16..6071ef3ccd9db24e4b160b4f268694e25afcc909 100644 --- a/docs/html/design/patterns/multi-pane-layouts.jd +++ b/docs/html/design/patterns/multi-pane-layouts.jd @@ -1,8 +1,8 @@ page.title=Multi-pane Layouts page.tags="tablet","navigation","layout","fragment" +page.metaDescription=Android devices come in many different screen sizes and types. Multi-pane layouts help you provide a balanced and aesthetically pleasing layout across the range of Android devices. @jd:body -

Developer Docs

@@ -10,9 +10,11 @@ page.tags="tablet","navigation","layout","fragment"
-

When writing an app for Android, keep in mind that Android devices come in many different screen -sizes and types. Make sure that your app consistently provides a balanced and aesthetically pleasing -layout by adjusting its content to varying screen sizes and orientations.

+

When writing an app for Android, keep in mind that Android devices +come in many different screen sizes and types. Make sure that your app consistently provides a +balanced and aesthetically pleasing layout by adjusting its content to varying screen sizes and +orientations.

+

Panels are a great way for your app to achieve this. They allow you to combine multiple views into one compound view when a lot of horizontal screen real estate is available and by splitting them up when less space is available.

diff --git a/docs/html/design/patterns/navigation.jd b/docs/html/design/patterns/navigation.jd index 6f2215a28b1d0a74993eb04bdb4d06c37ac05184..3e60f66fd8f87d113d48488b5663f9091467aee4 100644 --- a/docs/html/design/patterns/navigation.jd +++ b/docs/html/design/patterns/navigation.jd @@ -9,7 +9,7 @@ page.tags="navigation","activity","task","up navigation","back navigation"
-

Consistent navigation is an essential component of the overall user experience. Few things frustrate +

Consistent navigation is an essential component of the overall user experience. Few things frustrate users more than basic navigation that behaves in inconsistent and unexpected ways. Android 3.0 introduced significant changes to the global navigation behavior. Thoughtfully following the guidelines for Back and Up will make your app's navigation predictable and reliable for your users.

diff --git a/docs/html/design/patterns/notifications.jd b/docs/html/design/patterns/notifications.jd index 80f1b0e201128a897057d1c5b8db66b1d7c0b1b1..41f9190271067dfe8adb2547cce0ee4838dc580d 100644 --- a/docs/html/design/patterns/notifications.jd +++ b/docs/html/design/patterns/notifications.jd @@ -8,7 +8,7 @@ page.title=Notifications
-

The notification system allows your app to keep the user informed about events, such as new chat messages or a calendar event. Think of notifications as a news channel that alerts the user to important events as they happen or a log that chronicles events while the user is not paying attention.

+

The notification system allows your app to keep the user informed about events, such as new chat messages or a calendar event. Think of notifications as a news channel that alerts the user to important events as they happen or a log that chronicles events while the user is not paying attention.

New in Jelly Bean

In Jelly Bean, notifications received their most important structural and functional update since the beginning of Android.

diff --git a/docs/html/design/patterns/selection.jd b/docs/html/design/patterns/selection.jd index 973ffde9152d1940e4efcd616c043824a3c62cb6..d0ad8372fbda8fd0b2545f53cb563ff69358847b 100644 --- a/docs/html/design/patterns/selection.jd +++ b/docs/html/design/patterns/selection.jd @@ -35,9 +35,9 @@ the contextual action bar (CAB).

-

Using the contextual action bar (CAB)

-

The selection CAB is a temporary action bar that overlays your app's current action bar while data -is selected. It appears after the user long presses on a selectable data item.

+

Using the contextual action bar

+

The contextual action bar (CAB) is a temporary action bar that overlays your app's current action bar while data +is selected. It appears after the user long-presses on a selectable data item.

diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd index fa3e538c2c5c138540799674a8d7d451a2cc3082..7e0485f37a5cba958377ec72263c0199b559dc23 100644 --- a/docs/html/design/patterns/settings.jd +++ b/docs/html/design/patterns/settings.jd @@ -9,7 +9,7 @@ page.tags="preferences","sharedpreferences" -

Settings is a place in your app where users indicate their preferences for how your app should +

Settings is a place in your app where users indicate their preferences for how your app should behave. This benefits users because:

    diff --git a/docs/html/design/patterns/swipe-views.jd b/docs/html/design/patterns/swipe-views.jd index 4c9fb880ec713652a12a6148792a1a08a3d78acb..46d6ffb8c3afa3c1d0d53d5ffc19aaf86e24da9d 100644 --- a/docs/html/design/patterns/swipe-views.jd +++ b/docs/html/design/patterns/swipe-views.jd @@ -9,7 +9,7 @@ page.tags="viewpager","navigation","tabs" -

    Efficient navigation is one of the cornerstones of a well-designed app. While apps are generally +

    Efficient navigation is one of the cornerstones of a well-designed app. While apps are generally built in a hierarchical fashion, there are instances where horizontal navigation can flatten vertical hierarchies and make access to related data items faster and more enjoyable. Swipe views allow the user to efficiently move from item to item using a simple gesture and thereby make diff --git a/docs/html/design/patterns/widgets.jd b/docs/html/design/patterns/widgets.jd index 87ebbb9d47b30731e8d2e79ce27c952a116a548b..d08f178dda99747be897f85d0e3bf6253c3325a2 100644 --- a/docs/html/design/patterns/widgets.jd +++ b/docs/html/design/patterns/widgets.jd @@ -9,7 +9,7 @@ page.tags="appwidget","home" -

    Widgets are an essential aspect of home screen customization. You can imagine them as "at-a-glance" views of an app's most important data and functionality that is accessible right from the user's home screen. Users can move widgets across their home screen panels, and, if supported, resize them to tailor the amount of information within a widget to their preference.

    +

    Widgets are an essential aspect of home screen customization. You can imagine them as "at-a-glance" views of an app's most important data and functionality that is accessible right from the user's home screen. Users can move widgets across their home screen panels, and, if supported, resize them to tailor the amount of information within a widget to their preference.

    Widget types

    As you begin planning your widget, think about what kind of widget you're trying to build. Widgets typically fall into one of the following categories:

    diff --git a/docs/html/design/style/iconography.jd b/docs/html/design/style/iconography.jd index fe4a3f9bcd226afc1cc4587c40835c12610054c4..4559f00a77b5ad1212dc82f93ae9a8b0c793f784 100644 --- a/docs/html/design/style/iconography.jd +++ b/docs/html/design/style/iconography.jd @@ -1,5 +1,7 @@ page.title=Iconography page.tags="icons" +meta.tags="icons, googleplay, listing, branding" +page.titleFriendly=Guidelines for creating your app's icons @jd:body @@ -8,7 +10,7 @@ page.tags="icons"

    An icon is a graphic that takes up a small portion of screen real estate and provides a quick, intuitive representation of an action, a status, or an app.

    -

    When you design icons for your app, it's important to keep in mind that your +

    When you design icons for your app, it's important to keep in mind that your app may be installed on a variety of devices that offer a range of pixel densities, as mentioned in Devices diff --git a/docs/html/design/style/index.jd b/docs/html/design/style/index.jd index 74d085b759b05077488140d0bcbaaf9d641863ec..f88fdb8e6b74a8a690a67198fcdaccd528fe1d34 100644 --- a/docs/html/design/style/index.jd +++ b/docs/html/design/style/index.jd @@ -18,7 +18,8 @@ footer.hide=1

    diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd index 61a98b7ad8a827a95f2772bdf1b9b1383a892137..3f88b9d57bbd12638a73c128a264063758eee104 100644 --- a/docs/html/develop/index.jd +++ b/docs/html/develop/index.jd @@ -3,6 +3,7 @@ page.title=Develop header.hide=1 carousel=1 tabbedList=1 +excludeFromSuggestions=true @jd:body +

    +Android wearables provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.

    + + + +

    Here you’ll find some guidelines for designing great user experiences on the Android Wear +platform. Designing for Android Wear is substantially different than designing for phones or +tablets, so we’ll start by describing how your content can work in tandem with the overall +Android Wear vision. To better understand the user experience on Android Wear, also be sure +to read the UI Overview.

    + + + + + + +

    Android Wear experiences are:

    + +
      +
    • Contextually aware and smart. These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android wearables are aware of their situation and state, and helpfully display the right information at the right time. Timely, relevant, specific.
    • + +
    • Glanceable. Wearable devices are used all throughout the day, even when they sit in our peripheral vision. Effective apps provide the maximum payload of information with a minimum of fuss, optimized to provide tiny snippets of relevant information throughout the day. Short, sharp, immediate.
    • + +
    • Zero/low interaction. Staying true to the strengths afforded by a smaller form factor, Android Wear focuses on simple interactions, only requiring input by the user when absolutely necessary. Most inputs are based around touch swipes or voice, and inputs requiring fine-grained motor skills are avoided. Gestural, simple, fast.
    • + +
    • Helpful. Android Wear is like a great personal assistant: it knows you and your preferences, it only interrupts you when absolutely necessary, and it’s always on hand to provide a ready answer. Efficient, respectful, responsive.
    • +
    + + +

    +By providing a smart connection to the rest of the world while respecting the user’s attention, Android Wear feels personal and global, simple and smart, unobtrusive and ever-ready. Notifications that respect these principles will feel most at home in the overall Android Wear experience. +

    + + + +

    Notification UI Patterns

    + +

    Android notifications appear as cards in the main stream and form the core of the Android Wear experience. Many of the main Android Design guidelines for notifications apply in Android Wear. Be respectful of users' attention and aware of how unnecessary interruptions will reflect on your application’s reputation.

    + +

    Omit needless text from your notifications. Design for glanceability, not reading. Use words and phrases, not sentences. Show, don't tell: where possible use simple icons, glyphs, and visualizations to convey your message.

    + + +

    In some cases, particularly with messaging applications, cards will contain dynamic content which may not fit on a single screen. In these cases the content will be automatically truncated to fit on the card and the user may tap to expand, so the full message should be provided.

    + +

    Notification priority should reflect the urgency of your notification, with only time-sensitive notifications carrying a high priority. Active notifications – that is, those that cause the device to vibrate – should only be used in cases that need the user's urgent attention or action (e.g. a time-based reminder, a message from a friend). Non-urgent notifications (e.g. a transit times card, daily pedometer count, social network updates) should be silently added to the card stream.

    + + + + +

    Actions

    + + + +

    Actions appear to the right of your notification, allowing the user to act on your notification. Up to three actions are permitted. The most-used action should be placed first, so that it is a single swipe away from your content.

    + +

    Actions consist of an icon and a caption. Icons should be PNG files, white on transparent background, 64 × 64 DP. Captions should be verb-driven and short, and will be automatically truncated at one line.

    + +

    Actions are optional. Many useful notifications will not need to include actions at all.

    + +

    For developer details about action buttons, see Creating +Notifications for Android Wear.

    + + + + + + +

    Images

    + + + + +

    Images appear behind cards in the stream, providing context and additional glanceability. Your image should support the core message of the notification; for example, a card about a sports team could include the team color and logo; a message from a contact should display that person's profile photo.

    + +

    Bear in mind that the card will partially cover the lower part of the image. Images should be at least 320 × 320 pixels at hdpi. Image backgrounds move when horizontally swiped, so landscape-oriented images work better on notifications that include pages or actions.

    + +

    To add large images, use setLargeIcon() with any notification, as +shown in Creating +Notifications for Android Wear.

    + + + + + +

    Application Icons

    + + + +

    Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon using + setHintHideIcon(). +

    + + + + + + + +

    Pages

    + +

    Pages are additional cards that can appear to the right of your main card in the stream. If your core message is longer than a short snippet, do not sacrifice glanceability by packing a lot of information into your primary notification. Instead, use pages to provide additional content.

    + + + + + +

    Pages appear immediately to the right of the main notification card. They are typically used to provide additional details or alternate views of the main card’s content. For example:

    +
      +
    • A current weather card might provide an additional page showing a three-day forecast.
    • +
    • A next train departure card might provide an additional page showing subsequent departures times.
    • +
    • A daily step count card might provide an additional page showing the same measurement in calories and distance.
    • +
    + +

    There is no imposed limit on the number of pages you may add. However, notifications that provide actions should show no more than three pages to ensure that the actions remain easily accessible.

    + +

    Pages are optional. Many useful notifications will not need to include pages at all.

    + +

    For developer details about pages, see +described in Adding +Pages to a Notification.

    + + + + + +

    Notification Stacks

    + + + + +

    Stacks may be used to collect multiple notifications from the same application into a single stack of cards. Whereas pages are used to provide additional detail on a single notification, stacks are used to collect multiple sibling notifications together. A stack may be expanded by the user to access each individual card contained within.

    + +

    Stacks are a way of adding multiple useful notifications without overwhelming the user’s stream. If your application may produce multiple concurrent notifications, consider combining them into a stack.

    + +

    Each notification within a stack can contain separate pages and separate actions that are relevant to that specific notification. The user can access these actions after expanding that notification's card within the stack.

    + +

    For developer details about stacks, see +described in Stacking +Notifications.

    + + + + + + +

    Voice Replies

    + + + + + +

    Voice replies are primarily used by messaging applications to provide a hands-free way of dictating a short message. You can also provide a up to five suggested replies or “canned responses” that are useful in a wide range of cases. These canned responses can be tapped by the user, allowing for a fast method of sending simple replies in cases where speaking may not be desirable.

    + +

    You should attempt to cover a range of simple, neutral replies in your choices. Longer voice replies may be automatically truncated in the Voice reply UI.

    + +

    For developer details about enabling voice replies, see +described in Receiving Voice Input from +a Notification.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/html/wear/design/user-interface.html b/docs/html/wear/design/user-interface.html new file mode 100644 index 0000000000000000000000000000000000000000..c23d79cf4ff1c3dd37ebc3bc911938f50b835e00 --- /dev/null +++ b/docs/html/wear/design/user-interface.html @@ -0,0 +1,498 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +UI Overview | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    UI Overview

    + + + + + + +
    + + +
    + + +

    A new form factor deserves a new UI model. At a high level, the Android Wear UI consists of two +main spaces centered around the core functions of Suggest and +Demand. Your application will have an important role to play in both of these +spaces.

    + + + +

    Suggest: The Context Stream

    + +
    + + +
    + +

    The context stream is a vertical list of cards, each showing a useful or timely piece of +information. Much like Google Now on Android phones and tablets, users swipe vertically to navigate +from card to card for a brief and comprehensive update about what's important to them. Only one card +is displayed on screen at a time, and background images are used to provide additional visual +information. Your application can create cards and inject them into the stream when they are most +likely to be useful.

    + +

    Cards in the stream are more than simple notifications. They can be swiped horizontally to +reveal additional pages. Further horizontal swiping may reveal tappable buttons, allowing the user +to take action on the notification. Cards can also be dismissed by swiping left to right, removing +them from the stream until the next time they have useful information to display. +In the emulator, hovering the mouse over the top of the screen illuminates a blue bar at +the top of the device that takes you home when clicked.

    + + + +

    Demand: The Cue Card

    + +
    + + +
    + +

    For cases where the context stream can't anticipate what the user would like to do, the cue card +allows users to speak to their device. The cue card is opened by saying, "Ok Google" or by tapping +on the "g" icon on the home screen. Swiping up on the cue card shows a list of actions, which can +also be tapped.

    + +

    The list of actions includes Android intents for voice actions. The upcoming Android Wear SDK +will enable developers to match their applications to these intents so users can perform actions +using these voice commands. Multiple applications may register for a single voice intent, and users +will have the opportunity to choose which application they prefer to use.

    + + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/images/01_notifications.png b/docs/html/wear/images/01_notifications.png new file mode 100644 index 0000000000000000000000000000000000000000..f9729d21c5a83d22db983ab39f8ffb4c9de97092 Binary files /dev/null and b/docs/html/wear/images/01_notifications.png differ diff --git a/docs/html/wear/images/02_notifications.png b/docs/html/wear/images/02_notifications.png new file mode 100644 index 0000000000000000000000000000000000000000..417f4a6d75618de84c92c732ddeaa152801de2c9 Binary files /dev/null and b/docs/html/wear/images/02_notifications.png differ diff --git a/docs/html/wear/images/03_actions.png b/docs/html/wear/images/03_actions.png new file mode 100644 index 0000000000000000000000000000000000000000..2e2ab0d46aa25fdb490ecf49813dfeb1679fdf67 Binary files /dev/null and b/docs/html/wear/images/03_actions.png differ diff --git a/docs/html/wear/images/04_images.png b/docs/html/wear/images/04_images.png new file mode 100644 index 0000000000000000000000000000000000000000..5c136d62b757e405aae41d9e64870475574eebda Binary files /dev/null and b/docs/html/wear/images/04_images.png differ diff --git a/docs/html/wear/images/05_images.png b/docs/html/wear/images/05_images.png new file mode 100644 index 0000000000000000000000000000000000000000..54f28273cfdafc170be96c625d5c07d0f6efcc9c Binary files /dev/null and b/docs/html/wear/images/05_images.png differ diff --git a/docs/html/wear/images/06_images.png b/docs/html/wear/images/06_images.png new file mode 100644 index 0000000000000000000000000000000000000000..fb53af5ee458837a6a5ba627d6bd7750da9e3934 Binary files /dev/null and b/docs/html/wear/images/06_images.png differ diff --git a/docs/html/wear/images/07_appicons.png b/docs/html/wear/images/07_appicons.png new file mode 100644 index 0000000000000000000000000000000000000000..eb2867ff602dea83747c4a796149ff8dd466d96a Binary files /dev/null and b/docs/html/wear/images/07_appicons.png differ diff --git a/docs/html/wear/images/08_pages.png b/docs/html/wear/images/08_pages.png new file mode 100644 index 0000000000000000000000000000000000000000..9a8fb5177161d0e58ac333803eeb8c43a72a5f73 Binary files /dev/null and b/docs/html/wear/images/08_pages.png differ diff --git a/docs/html/wear/images/09_pages.png b/docs/html/wear/images/09_pages.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d90da1c860377e4714524950f02d54440564e2 Binary files /dev/null and b/docs/html/wear/images/09_pages.png differ diff --git a/docs/html/wear/images/10_pages.png b/docs/html/wear/images/10_pages.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5f4eee1f6059c532844619ae0d77e944fdac7f Binary files /dev/null and b/docs/html/wear/images/10_pages.png differ diff --git a/docs/html/wear/images/11_bundles_A.png b/docs/html/wear/images/11_bundles_A.png new file mode 100644 index 0000000000000000000000000000000000000000..7199a1f4bb8e542d7e710349c0e1910a7503921a Binary files /dev/null and b/docs/html/wear/images/11_bundles_A.png differ diff --git a/docs/html/wear/images/11_bundles_B.png b/docs/html/wear/images/11_bundles_B.png new file mode 100644 index 0000000000000000000000000000000000000000..bb751a27797cf54587b5d6f842b77acf3eabbabe Binary files /dev/null and b/docs/html/wear/images/11_bundles_B.png differ diff --git a/docs/html/wear/images/12_voicereply.png b/docs/html/wear/images/12_voicereply.png new file mode 100644 index 0000000000000000000000000000000000000000..04f08cfa65ad89ce7c73dfa956f0e5479458eac8 Binary files /dev/null and b/docs/html/wear/images/12_voicereply.png differ diff --git a/docs/html/wear/images/13_voicereply.png b/docs/html/wear/images/13_voicereply.png new file mode 100644 index 0000000000000000000000000000000000000000..2939cadabfe17703a42dc6335e280589c1f350e2 Binary files /dev/null and b/docs/html/wear/images/13_voicereply.png differ diff --git a/docs/html/wear/images/14_circle_voicereply.png b/docs/html/wear/images/14_circle_voicereply.png new file mode 100644 index 0000000000000000000000000000000000000000..15e27df4c5ef742c62a489ce068d115d07bbae6f Binary files /dev/null and b/docs/html/wear/images/14_circle_voicereply.png differ diff --git a/docs/html/wear/images/android-wear.png b/docs/html/wear/images/android-wear.png new file mode 100644 index 0000000000000000000000000000000000000000..081bc89c8990deaf452cb9c348e01fdae0f9ce9f Binary files /dev/null and b/docs/html/wear/images/android-wear.png differ diff --git a/docs/html/wear/images/blogger.png b/docs/html/wear/images/blogger.png new file mode 100644 index 0000000000000000000000000000000000000000..805958b9d5f798a6b13ffd5a5c294081e3b577b8 Binary files /dev/null and b/docs/html/wear/images/blogger.png differ diff --git a/docs/html/wear/images/carrot.png b/docs/html/wear/images/carrot.png new file mode 100644 index 0000000000000000000000000000000000000000..7f2b20a6fcb80366081e2085d3ae833877a113e1 Binary files /dev/null and b/docs/html/wear/images/carrot.png differ diff --git a/docs/html/wear/images/circle_badge_B.png b/docs/html/wear/images/circle_badge_B.png new file mode 100644 index 0000000000000000000000000000000000000000..8e0280a4ccb16d9a9b1bba85ac424cfb35587a69 Binary files /dev/null and b/docs/html/wear/images/circle_badge_B.png differ diff --git a/docs/html/wear/images/circle_email.png b/docs/html/wear/images/circle_email.png new file mode 100644 index 0000000000000000000000000000000000000000..ef835be606bfcdd88b6ca7fe7274d9de6cf499f5 Binary files /dev/null and b/docs/html/wear/images/circle_email.png differ diff --git a/docs/html/wear/images/circle_email_action.png b/docs/html/wear/images/circle_email_action.png new file mode 100644 index 0000000000000000000000000000000000000000..d0abd0f0637987b7f393565dcf1ad1de24881341 Binary files /dev/null and b/docs/html/wear/images/circle_email_action.png differ diff --git a/docs/html/wear/images/circle_message2.png b/docs/html/wear/images/circle_message2.png new file mode 100644 index 0000000000000000000000000000000000000000..63b783959f01f0718bd79bfca5c73276c39d75cd Binary files /dev/null and b/docs/html/wear/images/circle_message2.png differ diff --git a/docs/html/wear/images/circle_message2_reply.png b/docs/html/wear/images/circle_message2_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..1de6d214477f14719153b0e171a65de152c0cb40 Binary files /dev/null and b/docs/html/wear/images/circle_message2_reply.png differ diff --git a/docs/html/wear/images/circle_voice_A.png b/docs/html/wear/images/circle_voice_A.png new file mode 100644 index 0000000000000000000000000000000000000000..c4dd1adc92a0717fd2fdec7e595acc5404ce22c9 Binary files /dev/null and b/docs/html/wear/images/circle_voice_A.png differ diff --git a/docs/html/wear/images/circle_voice_B.png b/docs/html/wear/images/circle_voice_B.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa1d674fdb7c69b980af5e2f715f3f4a03bfc2b Binary files /dev/null and b/docs/html/wear/images/circle_voice_B.png differ diff --git a/docs/html/wear/images/close.png b/docs/html/wear/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..7e45fb71c3f819aad47b65dcd590601b0107d8b8 Binary files /dev/null and b/docs/html/wear/images/close.png differ diff --git a/docs/html/wear/images/features/s1.png b/docs/html/wear/images/features/s1.png new file mode 100644 index 0000000000000000000000000000000000000000..ba96cf8e59e22ba94e854e9abf2c83378dfd7d9e Binary files /dev/null and b/docs/html/wear/images/features/s1.png differ diff --git a/docs/html/wear/images/features/s2.png b/docs/html/wear/images/features/s2.png new file mode 100644 index 0000000000000000000000000000000000000000..af28496e550c7fcbcf638b8116feb6e26346ab08 Binary files /dev/null and b/docs/html/wear/images/features/s2.png differ diff --git a/docs/html/wear/images/features/s3.png b/docs/html/wear/images/features/s3.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae9868d99eda72c991c690580ea44ee7851a4e1 Binary files /dev/null and b/docs/html/wear/images/features/s3.png differ diff --git a/docs/html/wear/images/features/s4.png b/docs/html/wear/images/features/s4.png new file mode 100644 index 0000000000000000000000000000000000000000..125713d00e063726689c96533a34afaedee3065e Binary files /dev/null and b/docs/html/wear/images/features/s4.png differ diff --git a/docs/html/wear/images/features/ts1.png b/docs/html/wear/images/features/ts1.png new file mode 100644 index 0000000000000000000000000000000000000000..5d4b1c1555bd69736f17e6cd64acf5b1db166adf Binary files /dev/null and b/docs/html/wear/images/features/ts1.png differ diff --git a/docs/html/wear/images/features/ts2.png b/docs/html/wear/images/features/ts2.png new file mode 100644 index 0000000000000000000000000000000000000000..dc798c5d1a2b33b2ded245ec90ab41b2517f23af Binary files /dev/null and b/docs/html/wear/images/features/ts2.png differ diff --git a/docs/html/wear/images/features/ts3.png b/docs/html/wear/images/features/ts3.png new file mode 100644 index 0000000000000000000000000000000000000000..0d68ebc839d7e7923e6ad035f3af15366bd1e1fc Binary files /dev/null and b/docs/html/wear/images/features/ts3.png differ diff --git a/docs/html/wear/images/features/ts4.png b/docs/html/wear/images/features/ts4.png new file mode 100644 index 0000000000000000000000000000000000000000..e727ab57f9bf0670fb979e5c2bf3f3e45616ee47 Binary files /dev/null and b/docs/html/wear/images/features/ts4.png differ diff --git a/docs/html/wear/images/fitness-24.png b/docs/html/wear/images/fitness-24.png new file mode 100644 index 0000000000000000000000000000000000000000..3cf2f3cad10f4fec00ae8dd008d8fbed4c2b5147 Binary files /dev/null and b/docs/html/wear/images/fitness-24.png differ diff --git a/docs/html/wear/images/hero.jpg b/docs/html/wear/images/hero.jpg new file mode 100644 index 0000000000000000000000000000000000000000..40cc03c955caba5d023edc0ad79d87b0b72aeb70 Binary files /dev/null and b/docs/html/wear/images/hero.jpg differ diff --git a/docs/html/wear/images/kitchen_still.jpg b/docs/html/wear/images/kitchen_still.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4afe35932ef9330e6d4be653266199ba2ee81858 Binary files /dev/null and b/docs/html/wear/images/kitchen_still.jpg differ diff --git a/docs/html/wear/images/laptop-bridge.png b/docs/html/wear/images/laptop-bridge.png new file mode 100644 index 0000000000000000000000000000000000000000..b481224646f442c990c5bb81b83f84f3301ca62a Binary files /dev/null and b/docs/html/wear/images/laptop-bridge.png differ diff --git a/docs/html/wear/images/more_bottom.png b/docs/html/wear/images/more_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..632546aacf23ab8c776da6fe8dfe922a916da723 Binary files /dev/null and b/docs/html/wear/images/more_bottom.png differ diff --git a/docs/html/wear/images/more_mid.png b/docs/html/wear/images/more_mid.png new file mode 100644 index 0000000000000000000000000000000000000000..99bc99927ac3c4f11aefc1867d9198d379452095 Binary files /dev/null and b/docs/html/wear/images/more_mid.png differ diff --git a/docs/html/wear/images/more_top.png b/docs/html/wear/images/more_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8ead1d39392f89b305ed48d10c129f980ec7afe5 Binary files /dev/null and b/docs/html/wear/images/more_top.png differ diff --git a/docs/html/wear/images/notification_phone@2x.png b/docs/html/wear/images/notification_phone@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e1e429771bb5bbed21706524246c92e02c6d762a Binary files /dev/null and b/docs/html/wear/images/notification_phone@2x.png differ diff --git a/docs/html/wear/images/partners/asus.png b/docs/html/wear/images/partners/asus.png new file mode 100644 index 0000000000000000000000000000000000000000..37764b44d8698bc21a20ec0ed50e5f6e7d7d6780 Binary files /dev/null and b/docs/html/wear/images/partners/asus.png differ diff --git a/docs/html/wear/images/partners/broadcom.png b/docs/html/wear/images/partners/broadcom.png new file mode 100644 index 0000000000000000000000000000000000000000..b7171420ee4d2ff82e06926ff1cdfc3ae2632db6 Binary files /dev/null and b/docs/html/wear/images/partners/broadcom.png differ diff --git a/docs/html/wear/images/partners/fossil.png b/docs/html/wear/images/partners/fossil.png new file mode 100644 index 0000000000000000000000000000000000000000..cd311b64b7d30e199a2abaf382d4468f63253b48 Binary files /dev/null and b/docs/html/wear/images/partners/fossil.png differ diff --git a/docs/html/wear/images/partners/htc.png b/docs/html/wear/images/partners/htc.png new file mode 100644 index 0000000000000000000000000000000000000000..8236ac2f5769763c045bf3654a9ffadbd9e6f407 Binary files /dev/null and b/docs/html/wear/images/partners/htc.png differ diff --git a/docs/html/wear/images/partners/intel.png b/docs/html/wear/images/partners/intel.png new file mode 100644 index 0000000000000000000000000000000000000000..79ca5af26f7230187a557ff1da417bbaf4335f3d Binary files /dev/null and b/docs/html/wear/images/partners/intel.png differ diff --git a/docs/html/wear/images/partners/lg.png b/docs/html/wear/images/partners/lg.png new file mode 100644 index 0000000000000000000000000000000000000000..15d84e4d82c43077c486a8805ab37b2e80ec26ec Binary files /dev/null and b/docs/html/wear/images/partners/lg.png differ diff --git a/docs/html/wear/images/partners/mediatek.png b/docs/html/wear/images/partners/mediatek.png new file mode 100644 index 0000000000000000000000000000000000000000..2bf0f90544d3d77111503a8bc5343a21a04439aa Binary files /dev/null and b/docs/html/wear/images/partners/mediatek.png differ diff --git a/docs/html/wear/images/partners/mips.png b/docs/html/wear/images/partners/mips.png new file mode 100644 index 0000000000000000000000000000000000000000..04588f217e307d6cd0b47596f9992c205a3cd626 Binary files /dev/null and b/docs/html/wear/images/partners/mips.png differ diff --git a/docs/html/wear/images/partners/motorola.png b/docs/html/wear/images/partners/motorola.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1129fa06a928ca6026dc7297bad279ea1c56a4 Binary files /dev/null and b/docs/html/wear/images/partners/motorola.png differ diff --git a/docs/html/wear/images/partners/qualcomm.png b/docs/html/wear/images/partners/qualcomm.png new file mode 100644 index 0000000000000000000000000000000000000000..cb52a98f72b247ba302669d40800a45dd2a90498 Binary files /dev/null and b/docs/html/wear/images/partners/qualcomm.png differ diff --git a/docs/html/wear/images/partners/samsung.png b/docs/html/wear/images/partners/samsung.png new file mode 100644 index 0000000000000000000000000000000000000000..b39629e141e9ea27687d59119c3c622b7704fba4 Binary files /dev/null and b/docs/html/wear/images/partners/samsung.png differ diff --git a/docs/html/wear/images/screens/05_images.png b/docs/html/wear/images/screens/05_images.png new file mode 100644 index 0000000000000000000000000000000000000000..46ee5b3170edb4d9adf9f31ff54d1caf729cc43c Binary files /dev/null and b/docs/html/wear/images/screens/05_images.png differ diff --git a/docs/html/wear/images/screens/08_pages.png b/docs/html/wear/images/screens/08_pages.png new file mode 100644 index 0000000000000000000000000000000000000000..62f2a8dd8ad4a1fee29f82c638ca929ed7334d86 Binary files /dev/null and b/docs/html/wear/images/screens/08_pages.png differ diff --git a/docs/html/wear/images/screens/11_stack_B.png b/docs/html/wear/images/screens/11_stack_B.png new file mode 100644 index 0000000000000000000000000000000000000000..f28accb8d2b5ffe55b33731f455df56d96ef679a Binary files /dev/null and b/docs/html/wear/images/screens/11_stack_B.png differ diff --git a/docs/html/wear/images/screens/13_voicereply_02.png b/docs/html/wear/images/screens/13_voicereply_02.png new file mode 100644 index 0000000000000000000000000000000000000000..290c7b554ced0d28713c6ed2a9f83518d342883f Binary files /dev/null and b/docs/html/wear/images/screens/13_voicereply_02.png differ diff --git a/docs/html/wear/images/screens/14_circle_voicereply.png b/docs/html/wear/images/screens/14_circle_voicereply.png new file mode 100644 index 0000000000000000000000000000000000000000..b2845d5a3c750803b88ac562f459122150a2e771 Binary files /dev/null and b/docs/html/wear/images/screens/14_circle_voicereply.png differ diff --git a/docs/html/wear/images/screens/bezel.png b/docs/html/wear/images/screens/bezel.png new file mode 100644 index 0000000000000000000000000000000000000000..077a7e648bfd1c9ab56353ee8a2c95ade50c0f82 Binary files /dev/null and b/docs/html/wear/images/screens/bezel.png differ diff --git a/docs/html/wear/images/screens/circle_message2.png b/docs/html/wear/images/screens/circle_message2.png new file mode 100644 index 0000000000000000000000000000000000000000..da18b8d72cff7cd5b8ce085b93981b29a6d33a8b Binary files /dev/null and b/docs/html/wear/images/screens/circle_message2.png differ diff --git a/docs/html/wear/images/screens/circle_voice_B.png b/docs/html/wear/images/screens/circle_voice_B.png new file mode 100644 index 0000000000000000000000000000000000000000..d367c27ac911ca735ef0a258377666ed8a559340 Binary files /dev/null and b/docs/html/wear/images/screens/circle_voice_B.png differ diff --git a/docs/html/wear/images/screens/cuecard.gif b/docs/html/wear/images/screens/cuecard.gif new file mode 100644 index 0000000000000000000000000000000000000000..de1f53be113e292c579e94b05be3e8b91f05e030 Binary files /dev/null and b/docs/html/wear/images/screens/cuecard.gif differ diff --git a/docs/html/wear/images/screens/fitness-24.png b/docs/html/wear/images/screens/fitness-24.png new file mode 100644 index 0000000000000000000000000000000000000000..18ae969d1d7edd45ffb0fe0f3eeed73e4835c389 Binary files /dev/null and b/docs/html/wear/images/screens/fitness-24.png differ diff --git a/docs/html/wear/images/screens/pages.ogv b/docs/html/wear/images/screens/pages.ogv new file mode 100644 index 0000000000000000000000000000000000000000..56a15e1cb775b0645254b28168e3f5cab5a5f54f Binary files /dev/null and b/docs/html/wear/images/screens/pages.ogv differ diff --git a/docs/html/wear/images/screens/pages_animated.gif b/docs/html/wear/images/screens/pages_animated.gif new file mode 100644 index 0000000000000000000000000000000000000000..d236b0aafe8f39ecf4d018b6a64961e8e8ad1598 Binary files /dev/null and b/docs/html/wear/images/screens/pages_animated.gif differ diff --git a/docs/html/wear/images/screens/reservation_animated.gif b/docs/html/wear/images/screens/reservation_animated.gif new file mode 100644 index 0000000000000000000000000000000000000000..af98a3c57f859f30ef9bd649101090d16aedde2c Binary files /dev/null and b/docs/html/wear/images/screens/reservation_animated.gif differ diff --git a/docs/html/wear/images/screens/stream.gif b/docs/html/wear/images/screens/stream.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c18775882cc7697553bbd8750a7124a751b09f6 Binary files /dev/null and b/docs/html/wear/images/screens/stream.gif differ diff --git a/docs/html/wear/images/screens/voice_02.png b/docs/html/wear/images/screens/voice_02.png new file mode 100644 index 0000000000000000000000000000000000000000..c18a0cf488faa457516f454807cd734a23beafb3 Binary files /dev/null and b/docs/html/wear/images/screens/voice_02.png differ diff --git a/docs/html/wear/images/screens/yoga.gif b/docs/html/wear/images/screens/yoga.gif new file mode 100644 index 0000000000000000000000000000000000000000..a3c26be89b18d7798c35c3676b8322cbe2503156 Binary files /dev/null and b/docs/html/wear/images/screens/yoga.gif differ diff --git a/docs/html/wear/images/voice-23.png b/docs/html/wear/images/voice-23.png new file mode 100644 index 0000000000000000000000000000000000000000..06cba5b1b7ae8e9df141a538e79e83f161b40b3d Binary files /dev/null and b/docs/html/wear/images/voice-23.png differ diff --git a/docs/html/wear/index.html b/docs/html/wear/index.html new file mode 100644 index 0000000000000000000000000000000000000000..966046330441b2704714a11fe6aab5de8a6bcd66 --- /dev/null +++ b/docs/html/wear/index.html @@ -0,0 +1,739 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Android Wear | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + + +
    + + +
    + + + + + +
    +
    +
    +   +
    + +
    + +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    Android Wear
    +
    Information that moves with you
    +
    +
    +

    Small, powerful devices, worn on the body. + Useful information when you need it most. + Intelligent answers to spoken questions. + Tools to help reach fitness goals. + Your key to a multiscreen world.

    +
    + +
    + + Get the Developer Preview + + + + + +
    +
    + +
    +
    +
    +
    + + Scroll down to read more + +
    +
    +
    + +
    +
    +
    +
    +
    Extending Android to Wearables
    +
    + Android Wear extends the Android platform to a new generation of wearable devices.
    + The user experience is designed specifically for wearables. +
    +
    + +
    +
    +
    + +
    + + +
    + +

    + Say “Ok Google” to ask questions and get stuff done. +

    +
    +
    + Image of a Hangouts message +

    + Get glanceable, actionable information at just the right time throughout the day. +

    +
    +
    + Image showing +

    + A wide range of sensors is available to your applications, from accelerometers to heart rate monitors. +

    +
    +
    + +

    + The Android Wear Developer Preview lets you create wearable experiences for your existing Android apps and see how they will appear on square and round Android wearables. +

    + +

    + Later this year, we’ll be launching the Android Wear SDK, enabling even more customized experiences. +

    +
    +
    +
    + +
    +
    +
    +
    Developer Preview
    +
    + Your app’s notifications will already appear on Android wearables.
    + With the new Android Wear APIs you can customize and extend those notifications. +
    +
    + + +
    +
    +
    + +

    Receive Voice Replies

    +

    + Add actions to your notifications to allow users to reply by voice or touch. The system delivers the text to your app on the phone. +

    +

    + Learn about input actions +

    +
    +
    + + +
    + + +
    + +

    Add Notification Pages

    +

    + Add additional pages to your notification that are visible on the wearable device to provide detailed information on the wrist. +

    +

    + Learn about pages +

    +
    +
    + +

    Stack Multiple Notifications

    +

    + Your app should consolidate similar notifications. On a wearable, you can stack them together so the details for each are immediately available. +

    +

    + Learn about stacks +

    +
    +
    + +

    + You can also trigger your notifications contextually using existing Android APIs. For example, use geofences to provide glanceable information to your users when they are at home, or use the activity detection APIs to send messages to your users’ wrists while they are bicycling. +

    + +

    See the Android Wear Developer Preview Design Principles for more suggestions on creating great wearable experiences.

    + +
    +
    +
    + +
    +
    +
    +
    Coming soon
    +
    The Android Wear SDK
    +
    + The Developer Preview is just the beginning for Android Wear. +
    +
    + +
    +

    + In the coming months we’ll be launching new APIs and features for Android wearables to create even more unique experiences for the wrist: +

    + +
    +
    + +

    Build Custom UI

    +

    + Create custom card layouts and run activities directly on wearables. +

    +
    +
    + +

    Send Data

    +

    + Send data and actions between a phone and a wearable with data replication APIs and RPCs. +

    +
    +
    + +

    Control Sensors

    +

    + Gather sensor data and display it in real-time on Android wearables. +

    +
    +
    + +

    Voice Actions

    +

    + Register your app to handle voice actions, like "Ok Google, take a note." +

    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Building an Ecosystem
    +
    +

    + We’re working with several partners to bring you watches powered by Android Wear later this year! +

    +
    +
    + +
    +
    + Asus +
    +
    + Broadcom +
    +
    + Fossil +
    +
    + HTC +
    +
    + Intel +
    +
    + LG +
    +
    + Mediatek +
    +
    + MIPS +
    +
    + Motorola +
    +
    + Qualcomm +
    +
    + Samsung +
    +
    +
    +
    + +
    +
    +
    +
    Start working with Android Wear
    +
    +

    + Your app’s notifications will already appear on Android wearables.
    + With the new Android Wear APIs, you can customize and extend those notifications. +

    +

    + We’re excited about wearables and the experiences developers can create with them.
    + We can’t wait to see what you do next.

    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + + + +
    +

    DevBytes

    +

    + Learn how to optimize your app notifications for wearable devices in this DevBytes video using the Android Wear Developer Preview. +

    +
    +
    +
    + + + +
    +

    Blog Post

    +

    + Read more about the Android Wear Developer Preview announcement + at the Android Developers Blog. +

    +
    +
    +
    + + +Android Wear Developers + +
    +

    G+ Community

    +

    + Follow us on Google+ to stay up-to-date on Android Wear development and join the discussion! +

    +

    + +Android Wear Developers +

    +
    +
    +
    +
    +
    +
    +
    + + + + + + +
    + + + + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/license.html b/docs/html/wear/license.html new file mode 100644 index 0000000000000000000000000000000000000000..c7569bce3ae9d930a576339aac1bad7a9731b487 --- /dev/null +++ b/docs/html/wear/license.html @@ -0,0 +1,581 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Developer Preview License Agreement | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Developer Preview License Agreement

    + + + + + + +
    + + +
    +
    +This is the Android Wear Developer Preview License Agreement. + +1. Introduction + +1.1 The Android Wear Developer Preview Kit (referred to in this License Agreement as the “Developer Preview” and specifically including the Android system files, packaged APIs, Developer Preview library files, and the Developer Preview companion app, if and when they are made available) is licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Google in relation to your use of the Developer Preview. + +1.2 "Android Wear" means the Android Wear devices and the Android Wear software stack for use on Android Wear devices. + +1.3 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time. + +1.4 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States. + +2. Accepting this License Agreement + +2.1 In order to use the Developer Preview, you must first agree to this License Agreement. You may not use the Developer Preview if you do not accept this License Agreement. + +2.2 By clicking to accept, you hereby agree to the terms of this License Agreement. + +2.3 You may not use the Developer Preview and may not accept the License Agreement if you are a person barred from receiving the Developer Preview under the laws of the United States or other countries including the country in which you are resident or from which you use the Developer Preview. + +2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the Developer Preview on behalf of your employer or other entity. + +3. Developer Preview License from Google + +3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide, royalty-free, internal-use, non-assignable and non-exclusive license to use the Developer Preview solely to develop applications to run on the Android Wear platform for Android Wear devices. + +3.2 You agree that Google or third parties own all legal right, title and interest in and to the Developer Preview, including any Intellectual Property Rights that subsist in the Developer Preview. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you. + +3.3 You may not use the Developer Preview for any purpose not expressly permitted by this License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the Developer Preview or any part of the Developer Preview; or (b) load any part of the Developer Preview onto a mobile handset or wearable computing device or any other hardware device except an Android Wear device, combine any part of the Developer Preview with other software, or distribute any software or device incorporating a part of the Developer Preview. + +3.4 You agree that you will not take any actions that may cause or result in the fragmentation of Android Wear, including but not limited to distributing, participating in the creation of, or promoting in any way a software development kit derived from the Developer Preview. + +3.5 Use, reproduction and distribution of components of the Developer Preview licensed under an open source software license are governed solely by the terms of that open source software license and not this License Agreement. + +3.6 You agree that the form and nature of the Developer Preview that Google provides may change without prior notice to you and that future versions of the Developer Preview may be incompatible with applications developed on previous versions of the Developer Preview. You agree that Google may stop (permanently or temporarily) providing the Developer Preview (or any features within the Developer Preview) to you or to users generally at Google's sole discretion, without prior notice to you. + +3.7 Nothing in this License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. + +3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the Developer Preview. + +3.9 Your use of any Android system files, packaged APIs, or other components of the Developer Preview which are part of the Android Software Development Kit is subject to the terms of the Android Software Development Kit License Agreement located at http://developer.android.com/sdk/terms.html. These terms are hereby incorporated by reference into this License Agreement. + +4. Use of the Developer Preview by You + +4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under this License Agreement in or to any software applications that you develop using the Developer Preview, including any intellectual property rights that subsist in those applications. + +4.2 You agree to use the Developer Preview and write applications only for purposes that are permitted by (a) this License Agreement, (b) the Google Play Developer Program Policies located at https://play.google.com/about/developer-content-policy.html, and hereby incorporated into this License Agreement by reference), and (c) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). You agree to use reasonable efforts to comply with the Android Wear Platform Design Guide available on the Android Wear developer website + +4.3 You agree that if you use the Developer Preview to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so. + +4.4 You agree that you will not engage in any activity with the Developer Preview, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google. + +4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android Wear and/or applications for Android Wear, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so. + +4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach. + +4.7 Unless otherwise specified in writing by Google, Google does not intend use of Android Wear to create obligations under the Health Insurance Portability and Accountability Act, as amended, (“HIPAA”), and makes no representations that Android Wear satisfies HIPAA requirements. If you are (or become) a Covered Entity or Business Associate under HIPAA, you agree not to use Android Wear for any purpose or in any manner involving Protected Health Information unless you have received prior written consent to such use from Google. + +4.8 The Developer Preview is in development, and your testing and feedback are an important part of the development process. By using the Developer Preview, you acknowledge that implementation of some features are still under development and that you should not rely on the Developer Preview, Android Wear devices, Android Wear system software, or Android Wear services having the full functionality of a stable release. You agree not to publicly distribute or ship any application using this Developer Preview as this Developer Preview will no longer be supported after the official SDK is released. + +5. Your Developer Credentials + +5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials. + +6. Privacy and Information + +6.1 In order to continually innovate and improve the Developer Preview, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the Developer Preview are being used and how they are being used. Before any of this information is collected, the Developer Preview will notify you and seek your consent. If you withhold consent, the information will not be collected. + +6.2 The data collected is examined in the aggregate to improve the Developer Preview and is maintained in accordance with Google's Privacy Policy lcoated at http://www.google.com/policies/privacy/. + +7. Third Party Applications + +7.1 If you use the Developer Preview to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources. + +7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners. + +7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties. + +8. Using Google APIs + +8.1 Google APIs + +8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service. + +8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so. + +9. Terminating this License Agreement + +9.1 This License Agreement will continue to apply until terminated by either you or Google as set out below. + +9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the Developer Preview and any relevant developer credentials. + +9.3 Google may at any time, terminate this License Agreement with you if: +(A) you have breached any provision of this License Agreement; or +(B) Google is required to do so by law; or +(C) the partner with whom Google offered certain parts of Developer Preview (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the Developer Preview to you; or +(D) Google decides to no longer provide the Developer Preview or certain parts of the Developer Preview to users in the country in which you are resident or from which you use the service, or the provision of the Developer Preview or certain Developer Preview services to you by Google is, in Google's sole discretion, no longer commercially viable. + +9.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely. + +10. DISCLAIMER OF WARRANTIES + +10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE DEVELOPER PREVIEW IS AT YOUR SOLE RISK AND THAT THE DEVELOPER PREVIEW IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE. + +10.2 YOUR USE OF THE DEVELOPER PREVIEW AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE DEVELOPER PREVIEW IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. + +10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + +11. LIMITATION OF LIABILITY + +11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING. + +12. Indemnification + +12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys’ fees) arising out of or accruing from (a) your use of the Developer Preview, (b) any application you develop on the Developer Preview that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this License Agreement. + +13. Changes to the License Agreement + +13.1 Google may make changes to the License Agreement as it distributes new versions of the Developer Preview. When these changes are made, Google will make a new version of the License Agreement available on the website where the Developer Preview is made available. + +14. General Legal Terms + +14.1 This License Agreement constitutes the whole legal agreement between you and Google and governs your use of the Developer Preview (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the Developer Preview. + +14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google. + +14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable. + +14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to this License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to this License Agreement. + +14.5 EXPORT RESTRICTIONS. THE DEVELOPER PREVIEW IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE DEVELOPER PREVIEW. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE. + +14.6 The rights granted in this License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party. + +14.7 This License Agreement, and your relationship with Google under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction. +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/notifications/creating.html b/docs/html/wear/notifications/creating.html new file mode 100644 index 0000000000000000000000000000000000000000..5022c75a29e520b35ce4915db4044f94164931eb --- /dev/null +++ b/docs/html/wear/notifications/creating.html @@ -0,0 +1,722 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Creating Notifications for Android Wear | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Creating Notifications for Android Wear

    + + + + + + +
    + + +
    +

    When an Android device such as a phone or tablet is connected to an Android wearable, +all notifications are shared between the devices by default. On the Android wearable, each +notification appears as a new card in the context stream.

    + + + + +

    So without any effort, your app notifications are available to users on Android Wear. +However, you can enhance the user experience in several ways. For instance, +if users may respond to a notification by entering text, such as to reply to +a message, you can add the ability for users to reply by voice directly from the +wearable.

    + +

    To help you provide the best user experience +for your notifications on Android Wear, this guide shows you how to +build notifications using standard templates in +the NotificationCompat.Builder APIs, plus how to begin +extending your notification's capabilities for the wearable user experience.

    + +

    Note: +Notifications using RemoteViews are stripped of custom +layouts and the system uses only the text and icons in the +Notification object to +display the notification in a card. However, custom card layouts will be supported by +the official Android Wear SDK that is coming later.

    +
    + + + + +

    Import the Necessary Classes

    + +

    To begin development, you must first complete the instructions in the Get Started with the Developer Preview document. +As mentioned in that document, your app must include +both the v4 support +library and the Developer Preview support library. So to get started, +you should include the following imports in your project code:

    + +
    +import android.preview.support.wearable.notifications.*;
    +import android.preview.support.v4.app.NotificationManagerCompat;
    +import android.support.v4.app.NotificationCompat;
    +
    + +

    Caution: +The APIs in the current Android Wear Developer Preview are intended for development and testing purposes only, not for production apps. Google may change this Developer Preview significantly prior to the official release of the Android Wear SDK. You may not publicly distribute or ship any application using this Developer Preview, as this Developer Preview will no longer be supported after the official SDK is released (which will cause applications based only on the Developer Preview to break).

    + + + +

    Create Notifications with the Notification Builder

    + +

    The v4 +support library allows you to create notifications using the latest notification features +such as action buttons and large icons, while remaining compatible with Android 1.6 (API level +4) and higher.

    + + +

    For example, here's some code that creates and issues a notification using the +NotificationCompat APIs combined with the new + +NotificationManagerCompat API:

    + + +
    +int notificationId = 001;
    +// Build intent for notification content
    +Intent viewIntent = new Intent(this, ViewEventActivity.class);
    +viewIntent.putExtra(EXTRA_EVENT_ID, eventId);
    +PendingIntent viewPendingIntent =
    +        PendingIntent.getActivity(this, 0, viewIntent, 0);
    +
    +NotificationCompat.Builder notificationBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.ic_event)
    +        .setContentTitle(eventTitle)
    +        .setContentText(eventLocation)
    +        .setContentIntent(viewPendingIntent);
    +
    +// Get an instance of the NotificationManager service
    +NotificationManagerCompat notificationManager =
    +        NotificationManagerCompat.from(this);
    +
    +// Build the notification and issues it with notification manager.
    +notificationManager.notify(notificationId, notificationBuilder.build());
    +
    + +

    When this notification appears on a handheld device, the user can invoke the +PendingIntent +specified by the setContentIntent() method by touching the notification. When this +notification appears on an Android wearable, the user can swipe the notification to the left to +reveal the Open action, which invokes the intent on the handheld device.

    + + + + + + + + +

    Add Action Buttons

    + +

    In addition to the primary content action defined by +setContentIntent(), you can add other actions by passing a PendingIntent to +the addAction() method.

    + +

    For example, the following code shows the same type of notification from above, but adds an +action to view the event location on a map.

    + +
    +// Build an intent for an action to view a map
    +Intent mapIntent = new Intent(Intent.ACTION_VIEW);
    +Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
    +mapIntent.setData(geoUri);
    +PendingIntent mapPendingIntent =
    +        PendingIntent.getActivity(this, 0, mapIntent, 0);
    +
    +NotificationCompat.Builder notificationBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.ic_event)
    +        .setContentTitle(eventTitle)
    +        .setContentText(eventLocation)
    +        .setContentIntent(viewPendingIntent)
    +        .addAction(R.drawable.ic_map,
    +                getString(R.string.map), mapPendingIntent);
    +
    + +

    On a handheld device, the action appears as an +additional button attached to the notification. On an Android wearable, the action appears as +a large button when the user swipes the notification to the left. When the user taps the action, +the associated Intent is invoked on the handheld device.

    + +

    Tip: If your notifications includes a "Reply" action + (such as for a messaging app), you can enhance the behavior by enabling + voice input replies directly from the Android wearable. For more information, read + Receiving Voice Input from a Notification. +

    + +

    For details about designing action buttons (including the icon specifications), see the +Design Principles of Android +Wear.

    + + +

    Add a Big View

    + + + +

    You can insert extended text content +to your notification by adding one of the "big view" styles to your notification. On a +handheld device, users can see the big view content by expanding the notification, +while on Android Wear, the big view content is visible by default.

    + +

    To add the extended content to your notification, call setStyle() on the NotificationCompat.Builder object, passing it an instance of either +BigTextStyle or +InboxStyle.

    + +

    For example, the following code adds an instance of +NotificationCompat.BigTextStyle to the event notification, +in order to include the complete event description (which includes more text than can fit +into the space provided for setContentText()).

    + + +
    +// Specify the 'big view' content to display the long
    +// event description that may not fit the normal content text.
    +BigTextStyle bigStyle = new NotificationCompat.BigTextStyle();
    +bigStyle.bigText(eventDescription);
    +
    +NotificationCompat.Builder notificationBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.ic_event)
    +        .setLargeIcon(BitmapFractory.decodeResource(
    +                getResources(), R.drawable.notif_background))
    +        .setContentTitle(eventTitle)
    +        .setContentText(eventLocation)
    +        .setContentIntent(viewPendingIntent)
    +        .addAction(R.drawable.ic_map,
    +                getString(R.string.map), mapPendingIntent)
    +        .setStyle(bigStyle);
    +
    + +

    Notice that you can add a large background image to any notification using the +setLargeIcon() +method. For more information about designing notifications with large images, see the +Design Principles of Android +Wear.

    + + + +

    Add New Features for Wearables

    + +

    The Android Wear preview support library provides new APIs that + allow you to enhance the user experience for notifications on a wearable device. For example, + you can add additional pages of content that users can view by swiping to the left, or add the ability +for users to deliver your app a text response using voice input.

    + +

    To use these new APIs, pass your instance of +NotificationCompat.Builder to the + WearableNotifications.Builder() constructor. You can then add new +features to your notification using the + WearableNotifications.Builder methods. For example:

    + +
    +// Create a NotificationCompat.Builder for standard notification features
    +NotificationCompat.Builder notificationBuilder =
    +        new NotificationCompat.Builder(mContext)
    +        .setContentTitle("New mail from " + sender.toString())
    +        .setContentText(subject)
    +        .setSmallIcon(R.drawable.new_mail);
    +
    +// Create a WearablesNotification.Builder to add special functionality for wearables
    +Notification notification =
    +        new WearableNotifications.Builder(notificationBuilder)
    +        .setHintHideIcon(true)
    +        .build();
    +
    + +

    The + setHintHideIcon() method removes your app icon from the notification card. + This method is just one example of new notification features available from the + WearableNotifications.Builder class.

    + +

    When you want to deliver your notifications, be certain to always use the + + NotificationManagerCompat API:

    + +
    +// Get an instance of the NotificationManager service
    +NotificationManagerCompat notificationManager =
    +        NotificationManagerCompat.from(this);
    +
    +// Build the notification and issues it with notification manager.
    +notificationManager.notify(notificationId, notification);
    +
    + +

    If you instead use the framework's NotificationManager, some +features from WearableNotifications.Builder +will not work.

    + +

    To continue enhancing your notifications for wearables using + WearableNotifications.Builder and other APIs in the + preview support library, see the following developer guides:

    + +
    +
    Receiving Voice Input +from a Notification
    +
    Add an action that receives voice input from the user and delivers the +transcribed message to your app.
    +
    Adding Pages to a Notification
    +
    Add additional pages of information that are visible when the user +swipes to the left.
    +
    Stacking Notifications
    +
    Place all similar notifications from your app in a stack, allowing each to be +viewed individually without adding multiple cards to the card stream.
    +
    + + +
    + +
    +

    You might also want to read:

    +
    +
    Notifying the User
    +
    Learn more about how to create notifications.
    +
    Intents and Intent Filters
    +
    Learn everything you need to know about the Intent +APIs, used by notificaton actions.
    +
    +
    +
    + + + + + +
    + + + + + + +
    + + +
    + + + + + + + + + + + + + + + + + diff --git a/docs/html/wear/notifications/pages.html b/docs/html/wear/notifications/pages.html new file mode 100644 index 0000000000000000000000000000000000000000..ce568ebd7e1daa404011f61f5ab4812018538c4e --- /dev/null +++ b/docs/html/wear/notifications/pages.html @@ -0,0 +1,500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Adding Pages to a Notification | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Adding Pages to a Notification

    + + + + + + +
    + + +
    + + + +

    When you'd like to provide more information without requiring users +to open your app on their handheld device, you can +add one or more pages to the notification on Android Wear. The additional pages +appear immediately to the right of the main notification card. +For information about when to use and how to design +multiple pages, see the +Design Principles of Android +Wear.

    + + +

    When creating a notification with multiple pages, start by creating the main notification +(the first page) the way you'd like the notification to appear on a phone +or tablet. Then, add pages one at a time with the + +addPage() method, or add multiple pages in a Collection with the + +addPages() method.

    + + +

    For example, here's some code that adds a second page to a notification:

    + +
    +// Create builder for the main notification
    +NotificationCompat.Builder notificationBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.new_message)
    +        .setContentTitle("Page 1")
    +        .setContentText("Short message")
    +        .setContentIntent(viewPendingIntent);
    +
    +// Create a big text style for the second page
    +BigTextStyle secondPageStyle = new NotificationCompat.BigTextStyle();
    +secondPageStyle.setBigContentTitle("Page 2")
    +               .bigText("A lot of text...");
    +
    +// Create second page notification
    +Notification secondPageNotification =
    +        new NotificationCompat.Builder(this)
    +        .setStyle(secondPageStyle)
    +        .build();
    +
    +// Create main notification and add the second page
    +Notification twoPageNotification =
    +        new WearableNotifications.Builder(notificationBuilder)
    +        .addPage(secondPageNotification)
    +        .build();
    +
    + + + + + + + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/notifications/remote-input.html b/docs/html/wear/notifications/remote-input.html new file mode 100644 index 0000000000000000000000000000000000000000..c8f6621f9753a5b187c8bff566636b0ccf8b69a1 --- /dev/null +++ b/docs/html/wear/notifications/remote-input.html @@ -0,0 +1,652 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Receiving Voice Input from a Notification | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Receiving Voice Input from a Notification

    + + + + + + +
    + + +
    + + + + +

    If your notification includes an action to respond with text, + such as to reply to an email, it should normally launch an activity + on the handheld device. However, when your notification appears on an Android wearable, you can + allow users to dictate a reply with voice input. You can also provide pre-defined text + messages for the user to select.

    + +

    When the user replies with voice or selects one of the available +messages, the system sends the message to your app on the connected handheld device. +The message is attached as an extra in the Intent you specified +to be used for the notification action.

    + +

    Note: When developing with the Android emulator, +you must type text replies into the voice input field, so be sure you have enabled +Hardware keyboard present in the AVD settings.

    + + +

    Define the Remote Input

    + +

    To create an action that supports voice input, first create an instance of + +RemoteInput using the + RemoteInput.Builder APIs. + The + RemoteInput.Builder constructor takes a string that the system + will use as a key for the Intent extra that carries the reply message + to your app on the handheld.

    + +

    For example, here's how to create a new + +RemoteInput object that provides a custom + label for the voice input prompt:

    + +
    +// Key for the string that's delivered in the action's intent
    +private static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
    +
    +String replyLabel = getResources().getString(R.string.reply_label);
    +
    +RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
    +        .setLabel(replyLabel)
    +        .build();
    +
    + + +

    Add Pre-defined Text Responses

    + + + +

    In addition to allowing voice input, you can + provide up to five text responses that the user can select for quick replies. Call + setChoices() and pass it a string array.

    + +

    For example, you may define some responses in a resource array:

    + +

    res/values/strings.xml +

    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +    <string-array name="reply_choices">
    +        <item>Yes</item>
    +        <item>No</item>
    +        <item>Maybe</item>
    +    </string-array>
    +</resources>
    +
    + +

    Then, inflate the string array and add it to the + RemoteInput:

    + +
    +String replyLabel = getResources().getString(R.string.reply_label);
    +String[] replyChoices = getResources().getStringArray(R.array.reply_choices);
    +
    +RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
    +        .setLabel(replyLabel)
    +        .setChoices(replyChoices)
    +        .build();
    +
    + + + + +

    Receive Voice Input for the Primary Action

    + +

    If "Reply" is your notification's primary action (defined by the setContentIntent() +method), then you should attach the + RemoteInput to the main action using + +addRemoteInputForContentIntent(). For example:

    + +
    +// Create intent for reply action
    +Intent replyIntent = new Intent(this, ReplyActivity.class);
    +PendingIntent replyPendingIntent =
    +        PendingIntent.getActivity(this, 0, replyIntent, 0);
    +
    +// Build the notification
    +NotificationCompat.Builder replyNotificationBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.ic_new_message)
    +        .setContentTitle("Message from Travis")
    +        .setContentText("I love key lime pie!")
    +        .setContentIntent(replyPendingIntent);
    +
    +// Create the remote input
    +RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
    +        .setLabel(replyLabel)
    +        .build();
    +
    +// Create wearable notification and add remote input
    +Notification replyNotification =
    +        new WearableNotifications.Builder(replyNotificationBuilder)
    +        .addRemoteInputForContentIntent(replyAction)
    +        .build();
    +
    + + +

    By using + +addRemoteInputForContentIntent() to add the + RemoteInput object to the notification's primary action, +the button that normally appears as an "Open" action becomes the "Reply" action +and starts the voice input UI when users select it on Android Wear.

    + + + +

    Receive Voice Input for a Secondary Action

    + +

    If the "Reply" action is not your notification's primary action and you want to enable +voice input for a secondary action, add the + RemoteInput to a new action button defined by an + +Action object.

    + +

    You should instantiate the + +Action with the + Action.Builder() +constructor, which takes an icon and text label for the action button, plus the +PendingIntent +the system should use to invoke your app when the user selects the action. For example:

    + +
    +// Create the pending intent to fire when the user selects the action
    +Intent replyIntent = new Intent(this, ReplyActivity.class);
    +PendingIntent pendingReplyIntent =
    +        PendingIntent.getActivity(this, 0, replyIntent, 0);
    +
    +// Create the remote input
    +RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
    +        .setLabel(replyLabel)
    +        .build();
    +
    +// Create the notification action
    +Action replyAction = new Action.Builder(R.drawable.ic_message,
    +        "Reply", pendingIntent)
    +        .addRemoteInput(remoteInput)
    +        .build();
    +
    + + +

    After you add the + RemoteInput to the + +Action, add the + +Action to the + WearableNotifications.Builder using + addAction(). +For example:

    + +
    +// Create basic notification builder
    +NotificationCompat.Builder replyNotificationBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setContentTitle("New message");
    +
    +// Create the notification action and add remote input
    +Action replyAction = new Action.Builder(R.drawable.ic_message,
    +        "Reply", pendingIntent)
    +        .addRemoteInput(remoteInput)
    +        .build();
    +
    +// Create wearable notification and add action
    +Notification replyNotification =
    +        new WearableNotifications.Builder(replyNotificationBuilder)
    +        .addAction(replyAction)
    +        .build();
    +
    + +

    Now, when the user selects "Reply" from an Android wearable, the system prompts the user + for voice input (and shows the list of pre-defined replies, if provided). + Once the user completes a response, the system invokes + the Intent attached to the action and adds the +EXTRA_VOICE_REPLY extra (the string + you passed to the + RemoteInput.Builder constructor) + with the user's message as the string value.

    + + + + + + + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/notifications/stacks.html b/docs/html/wear/notifications/stacks.html new file mode 100644 index 0000000000000000000000000000000000000000..e4f74a037b10c7328ade7ba61efd185be4fc35f8 --- /dev/null +++ b/docs/html/wear/notifications/stacks.html @@ -0,0 +1,512 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Stacking Notifications | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Stacking Notifications

    + + + + + + +
    + + +
    + + + +

    When creating notifications for a handheld device, you should always aggregate similar +notifications into a single summary notification. For example, if your app creates notifications +for received messages, you should not show more than one notification +on a handheld device—when more than one is message is received, use a single notification +to provide a summary such as "2 new messages."

    + +

    However, a summary notification is less useful on an Android wearable because users +are not able to read details from each message on the wearable (they must open your app on the +handheld to view more information). So for the wearable device, you should +group all the notifications together in a stack. The stack of notifications appears as a single +card, which users can expand to view the details from each notification separately. The new + +setGroup() method makes this possible while allowing you to still provide +only one summary notification on the handheld device.

    + +

    For details about designing notification stacks, see the +Design Principles of Android +Wear.

    + + +

    Add Each Notification to a Group

    + +

    To create a stack, call +setGroup() for each notification you want in the stack, passing the same +group key. For example:

    + +
    +final static String GROUP_KEY_EMAILS = "group_key_emails";
    +
    +NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
    +         .setContentTitle("New mail from " + sender)
    +         .setContentText(subject)
    +         .setSmallIcon(R.drawable.new_mail);
    +
    +Notification notif = new WearableNotifications.Builder(builder)
    +         .setGroup(GROUP_KEY_EMAILS)
    +         .build();
    +
    + +

    By default, notifications appear in the order in which you added them, with the most recent + notification visible at the top. You can define a specific position in the group + by passing an order position as the second parameter for +setGroup().

    + + +

    Add a Summary Notification

    + +

    It's important that you still provide a summary notification that appears on handheld devices. +So in addition to adding each unique notification to the same stack group, also add a summary +notification, but set its order position to be GROUP_ORDER_SUMMARY.

    + +
    +Notification summaryNotification = new WearableNotifications.Builder(builder)
    +         .setGroup(GROUP_KEY_EMAILS, WearableNotifications.GROUP_ORDER_SUMMARY)
    +         .build();
    +
    + +

    This notification will not appear in your stack of notifications on the wearable, but +appears as the only notification on the handheld device. + + + + +

    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/preview/signup.html b/docs/html/wear/preview/signup.html new file mode 100644 index 0000000000000000000000000000000000000000..ca50179e6f883b46d3a52a436f69197ab62c4a32 --- /dev/null +++ b/docs/html/wear/preview/signup.html @@ -0,0 +1,609 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Sign Up for the Developer Preview | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Sign Up for the Developer Preview

    + + + + + + +
    + + +
    +

    To get started with the Android Wear Developer Preview, you must agree to the + following terms and conditions and provide the email address for your Google account. +After signing up, you’ll have access to:

    +
      +
    • New APIs that allow you to build enhanced notifications for wearables.
    • +
    • Sample code using the new APIs.
    • +
    • The Android Wear Preview app that delivers your notifications to the Android Wear emulator.
    • +
    + +
    +This is the Android Wear Developer Preview License Agreement. + +1. Introduction + +1.1 The Android Wear Developer Preview Kit (referred to in this License Agreement as the “Developer Preview” and specifically including the Android system files, packaged APIs, Developer Preview library files, and the Developer Preview companion app, if and when they are made available) is licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Google in relation to your use of the Developer Preview. + +1.2 "Android Wear" means the Android Wear devices and the Android Wear software stack for use on Android Wear devices. + +1.3 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time. + +1.4 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States. + +2. Accepting this License Agreement + +2.1 In order to use the Developer Preview, you must first agree to this License Agreement. You may not use the Developer Preview if you do not accept this License Agreement. + +2.2 By clicking to accept, you hereby agree to the terms of this License Agreement. + +2.3 You may not use the Developer Preview and may not accept the License Agreement if you are a person barred from receiving the Developer Preview under the laws of the United States or other countries including the country in which you are resident or from which you use the Developer Preview. + +2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the Developer Preview on behalf of your employer or other entity. + +3. Developer Preview License from Google + +3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide, royalty-free, internal-use, non-assignable and non-exclusive license to use the Developer Preview solely to develop applications to run on the Android Wear platform for Android Wear devices. + +3.2 You agree that Google or third parties own all legal right, title and interest in and to the Developer Preview, including any Intellectual Property Rights that subsist in the Developer Preview. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you. + +3.3 You may not use the Developer Preview for any purpose not expressly permitted by this License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the Developer Preview or any part of the Developer Preview; or (b) load any part of the Developer Preview onto a mobile handset or wearable computing device or any other hardware device except an Android Wear device, combine any part of the Developer Preview with other software, or distribute any software or device incorporating a part of the Developer Preview. + +3.4 You agree that you will not take any actions that may cause or result in the fragmentation of Android Wear, including but not limited to distributing, participating in the creation of, or promoting in any way a software development kit derived from the Developer Preview. + +3.5 Use, reproduction and distribution of components of the Developer Preview licensed under an open source software license are governed solely by the terms of that open source software license and not this License Agreement. + +3.6 You agree that the form and nature of the Developer Preview that Google provides may change without prior notice to you and that future versions of the Developer Preview may be incompatible with applications developed on previous versions of the Developer Preview. You agree that Google may stop (permanently or temporarily) providing the Developer Preview (or any features within the Developer Preview) to you or to users generally at Google's sole discretion, without prior notice to you. + +3.7 Nothing in this License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. + +3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the Developer Preview. + +3.9 Your use of any Android system files, packaged APIs, or other components of the Developer Preview which are part of the Android Software Development Kit is subject to the terms of the Android Software Development Kit License Agreement located at http://developer.android.com/sdk/terms.html. These terms are hereby incorporated by reference into this License Agreement. + +4. Use of the Developer Preview by You + +4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under this License Agreement in or to any software applications that you develop using the Developer Preview, including any intellectual property rights that subsist in those applications. + +4.2 You agree to use the Developer Preview and write applications only for purposes that are permitted by (a) this License Agreement, (b) the Google Play Developer Program Policies located at https://play.google.com/about/developer-content-policy.html, and hereby incorporated into this License Agreement by reference), and (c) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). You agree to use reasonable efforts to comply with the Android Wear Platform Design Guide available on the Android Wear developer website + +4.3 You agree that if you use the Developer Preview to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so. + +4.4 You agree that you will not engage in any activity with the Developer Preview, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google. + +4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android Wear and/or applications for Android Wear, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so. + +4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach. + +4.7 Unless otherwise specified in writing by Google, Google does not intend use of Android Wear to create obligations under the Health Insurance Portability and Accountability Act, as amended, (“HIPAA”), and makes no representations that Android Wear satisfies HIPAA requirements. If you are (or become) a Covered Entity or Business Associate under HIPAA, you agree not to use Android Wear for any purpose or in any manner involving Protected Health Information unless you have received prior written consent to such use from Google. + +4.8 The Developer Preview is in development, and your testing and feedback are an important part of the development process. By using the Developer Preview, you acknowledge that implementation of some features are still under development and that you should not rely on the Developer Preview, Android Wear devices, Android Wear system software, or Android Wear services having the full functionality of a stable release. You agree not to publicly distribute or ship any application using this Developer Preview as this Developer Preview will no longer be supported after the official SDK is released. + +5. Your Developer Credentials + +5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials. + +6. Privacy and Information + +6.1 In order to continually innovate and improve the Developer Preview, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the Developer Preview are being used and how they are being used. Before any of this information is collected, the Developer Preview will notify you and seek your consent. If you withhold consent, the information will not be collected. + +6.2 The data collected is examined in the aggregate to improve the Developer Preview and is maintained in accordance with Google's Privacy Policy lcoated at http://www.google.com/policies/privacy/. + +7. Third Party Applications + +7.1 If you use the Developer Preview to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources. + +7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners. + +7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties. + +8. Using Google APIs + +8.1 Google APIs + +8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service. + +8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so. + +9. Terminating this License Agreement + +9.1 This License Agreement will continue to apply until terminated by either you or Google as set out below. + +9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the Developer Preview and any relevant developer credentials. + +9.3 Google may at any time, terminate this License Agreement with you if: +(A) you have breached any provision of this License Agreement; or +(B) Google is required to do so by law; or +(C) the partner with whom Google offered certain parts of Developer Preview (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the Developer Preview to you; or +(D) Google decides to no longer provide the Developer Preview or certain parts of the Developer Preview to users in the country in which you are resident or from which you use the service, or the provision of the Developer Preview or certain Developer Preview services to you by Google is, in Google's sole discretion, no longer commercially viable. + +9.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely. + +10. DISCLAIMER OF WARRANTIES + +10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE DEVELOPER PREVIEW IS AT YOUR SOLE RISK AND THAT THE DEVELOPER PREVIEW IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE. + +10.2 YOUR USE OF THE DEVELOPER PREVIEW AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE DEVELOPER PREVIEW IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. + +10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + +11. LIMITATION OF LIABILITY + +11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING. + +12. Indemnification + +12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys’ fees) arising out of or accruing from (a) your use of the Developer Preview, (b) any application you develop on the Developer Preview that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this License Agreement. + +13. Changes to the License Agreement + +13.1 Google may make changes to the License Agreement as it distributes new versions of the Developer Preview. When these changes are made, Google will make a new version of the License Agreement available on the website where the Developer Preview is made available. + +14. General Legal Terms + +14.1 This License Agreement constitutes the whole legal agreement between you and Google and governs your use of the Developer Preview (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the Developer Preview. + +14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google. + +14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable. + +14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to this License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to this License Agreement. + +14.5 EXPORT RESTRICTIONS. THE DEVELOPER PREVIEW IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE DEVELOPER PREVIEW. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE. + +14.6 The rights granted in this License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party. + +14.7 This License Agreement, and your relationship with Google under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction. + + +
    + + +

    + Important: Your email address is used to provide your Google account + access to the Android Wear Preview app Beta Preview on Google Play Store. As such, the + email address you provide below must be for the account you use to download apps on Google Play Store. + We may also use your email address to provide you with updates about the Android Wear + platform release. +

    + + + + + + + + + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/html/wear/preview/start.html b/docs/html/wear/preview/start.html new file mode 100644 index 0000000000000000000000000000000000000000..b1861f5f36baab425260283b8020c6ca7f181283 --- /dev/null +++ b/docs/html/wear/preview/start.html @@ -0,0 +1,693 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Get Started with the Developer Preview | Android Developers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + + + + + +

    Get Started with the Developer Preview

    + + + + + + +
    + + +
    +
    + +
    +

    The Android Wear Developer Preview includes tools and APIs that allow you to +enhance your app notifications +to provide an optimized user experience on Android wearables.

    + +

    With the Android Wear Developer Preview, you can:

    + +
      +
    • Run the Android Wear platform in the Android emulator.
    • +
    • Connect your Android device to the emulator and view notifications from the +device as cards on Android Wear.
    • +
    • Try new APIs in the preview support library that enhance your app's notifications +with features such as voice replies and notification pages.
    • +
    + +

    To get access to the Developer Preview tools, +click the sign up button on the right, then follow the setup instructions below.

    +
    + +
    + + +Sign Up for the Developer Preview + +

    Signing up provides you access to:

    +
      +
    • New notification APIs in the preview support library.
    • +
    • Sample apps using the new notification APIs.
    • +
    • The Android Wear Preview app for your mobile device, which connects +your device to the Android Wear emulator.
    • +
    + +
    +
    + + +

    Caution: +The current Android Wear Developer Preview is intended for development and testing purposes only, not for production apps. Google may change this Developer Preview significantly prior to the official release of the Android Wear SDK. You may not publicly distribute or ship any application using this Developer Preview, as this Developer Preview will no longer be supported after the official SDK is released (which will cause applications based only on the Developer Preview to break).

    + + + + +

    Prerequisites

    + +

    Before you begin the setup, you must:

    + +
      +
    1. Install the Android SDK. +

      The Android SDK includes all the developer tools required to build +apps for Android (optional IDEs are also available for download).

    2. +
    3. Sign up for the Android Wear Developer Preview. +

      You must sign up with a Gmail or other Google account in order to download the +preview support library and receive access to the +Android Wear Preview beta app on Google Play Store.

    4. +
    + +

    Note: +If you're using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher. +If you're using Android Studio, you must update to version 0.5.1 or higher

    + + + +

    1. Install the Android Wear System Image

    + + +
      +
    1. Launch Android SDK Manager. +
        +
      • From Eclipse, select Window > Android SDK Manager.
      • +
      • From Android Studio, select Tools > Android > SDK Manager.
      • +
      +
    2. +
    3. Below Tools, verify that you have Android SDK Tools revision 22.6 or higher. +

      If your version of Android SDK Tools is lower than 22.6, you must update:

      +
        +
      1. Select Android SDK Tools.
      2. +
      3. Click Install package.
      4. +
      5. Accept the license and click Install.
      6. +
      7. When the installation completes, restart Android SDK Manager.
      8. +
      +
    4. + +
    5. Below Android 4.4.2, select Android Wear ARM EABI v7a System Image. +

      Note: Android Wear is designed to support multiple processor architectures. +

    6. +
    7. Below Extras, ensure that you have the latest version of the +Android Support Library. + If an update is available, select Android Support Library. If you're using Android Studio, also select Android Support Repository.
    8. +
    9. Click Install packages.
    10. +
    11. Accept the license and click Install.
    12. +
    + + + +

    2. Set Up the Android Wear Emulator

    + +
      +
    1. Launch the Android Virtual Device Manager. +
        +
      • From Eclipse, select Window > Android Virtual Device Manager.
      • +
      • From Android Studio, select Tools > Android > AVD Manager.
      • +
      +
    2. +
    3. Click New.
    4. +
    5. For the AVD Name, enter "AndroidWearSquare" or "AndroidWearRound", depending on whether +you want to create an emulator with a square or round display.
    6. +
    7. For the Device, select Android Wear Square or + Android Wear Round.
    8. +
    9. For the Target, select Android 4.4.2 - API Level 19 (or higher).
    10. +
    11. For the CPU/ABI, select Android Wear ARM (armeabi-v7a). +

      Note: Android Wear is designed to support multiple processor architectures. +

    12. +
    13. For the Skin, select AndroidWearSquare or +AndroidWearRound.
    14. +
    15. Leave all other options set to their defaults and click OK. +

      Although real Android wearables do not provide a keyboard as an input method, + you should keep Hardware keyboard present selected so you can + provide text input on screens where users will instead provide voice input.

      +
    16. + +
    17. In the list of AVDs, select the one you just created and click + Start. In the following window, click Launch.
    18. +
    + +

    The Android Wear emulator now starts. To begin testing your app's notifications, +you must now pair the emulator to your development device +that has the Android Wear Preview app installed.

    + +

    Tip: To improve the emulator startup time, edit your AVD +and enable Snapshot under Emulator Options. When you start the emulator, +select Save to snapshot then click Launch. Once the emulator +is running, close it to save a snapshot of the system. +Start the AVD again, but select Launch from snapshot and +deselect Save to snapshot.

    + +

    Caution: Do not install apps on the Android Wear emulator. +The system does not support traditional Android apps and the result of running such apps is +unpredictable.

    + + + +

    3. Set Up the Android Wear Preview App

    + +

    To view your app's notifications on the Android Wear emulator, you must have the +Android Wear Preview app installed on your Android device (a phone or tablet).

    + +

    To receive the Android Wear Preview app, you must sign up for the Developer Preview using the same +Gmail or Google account you use with Google Play Store.

    +

    + +

    Note: The Android Wear Preview app is compatible with + Android 4.3 and higher and is not available for the Android emulator.

    + +

    After you've signed up for the Developer Preview, + you'll receive a confirmation email that includes a link to opt-in to the + Android Wear Preview app beta program. Once you opt-in, it may take up to 24 hours for the + app to become available in Google Play Store.

    + +

    After you install the Android Wear Preview app, you can set up + your device to communicate with the Android Wear emulator:

    + +
      +
    1. Open the Android Wear Preview app. You should see a notice that the app is currently + not enabled as a notification listener. Tap the message to open the system settings, + then select Android Wear Preview to grant it notification access.
    2. +
    3. Connect your device to your development machine over USB. Be sure that no other + Android devices are connected to the machine.
    4. +
    5. Ensure that the Android Wear emulator (created in the previous section) is running. +The emulator should show the time and an icon that indicates no device is connected.
    6. +
    7. Open a command line terminal, navigate to your Android SDK's platform-tools/ +directory, then execute: +
      adb -d forward tcp:5601 tcp:5601
      +

      Note: You must execute this command each time you connect your +device over USB.

      +
    8. +
    9. Return to the Android Wear Preview app. It should now indicate that it is connected to + the emulator. The Android Wear emulator should now show the 'g' orb icon, indicating + that is is connected to your device. +
    + +

    Now, notifications from your device also appear in the Android Wear emulator.

    + + + + +

    4. Add the Support Library to Your Project

    + +

    The Android Wear preview support library includes several APIs that allow you to +optimize your app's notifications for the Android Wear user experience.

    + +

    To receive the preview support library, you must sign up for the Developer Preview. The +confirmation email you receive after you sign up includes a link to download a ZIP file, +which contains the preview support library and some sample apps.

    + +

    After you download and unzip the package, add the preview support library +sto your Android project:

    + +

    If you're using Eclipse:

    +
      +
    1. In your Android app project, create a libs/ directory in your project root + (the same location as the AndroidManifest.xml file).
    2. +
    3. Copy the v4 support library JAR file from your Android SDK directory (e.g., + <sdk>/extras/android/support/v4/android-support-v4.jar) into your + project libs/ directory. +
    4. Also save the wearable-preview-support.jar file in the libs/ directory. +
    5. Right click each JAR file and select Build Path > Add to Build Path.
    6. +
    + +

    If you're using Android Studio:

    +
      +
    1. In your Android app project, create a libs/ directory in your project root + (the same location as the AndroidManifest.xml file).
    2. +
    3. Save the wearable-preview-support.jar file in the libs/ directory. +
    4. Open the build.gradle file in your app module.
    5. +
    6. Add a dependency rule for both the v4 support library and the Android Wear + preview support library: +
      +dependencies {
      +    compile "com.android.support:support-v4:18.0.+"
      +    compile files('../libs/wearable-preview-support.jar')
      +}
      +
      +
    7. +
    8. Click Sync Project with Gradle Files in the toolbar.
    9. +
    + +

    To start optimizing your notifications for Android Wear, + read Creating Notifications for Android Wear.

    + + + + + + +
    + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + diff --git a/docs/image_sources/training/game-controllers/backward-compatible-inputmanager.graffle b/docs/image_sources/training/game-controllers/backward-compatible-inputmanager.graffle new file mode 100644 index 0000000000000000000000000000000000000000..fbf78be964e05b30260c89560a75d9c425fa20f4 --- /dev/null +++ b/docs/image_sources/training/game-controllers/backward-compatible-inputmanager.graffle @@ -0,0 +1,1076 @@ + + + + + ActiveLayerIndex + 0 + ApplicationVersion + + com.omnigroup.OmniGrafflePro + 139.18.0.187838 + + AutoAdjust + + BackgroundGraphic + + Bounds + {{0, 0}, {756, 553}} + Class + SolidGraphic + ID + 2 + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + + BaseZoom + 0 + CanvasOrigin + {0, 0} + ColumnAlign + 1 + ColumnSpacing + 36 + CreationDate + 2013-10-03 20:37:16 +0000 + Creator + Quddus Chong + DisplayScale + 1 0/72 in = 1.0000 in + GraphDocumentVersion + 8 + GraphicsList + + + Bounds + {{69.703689575195312, 209}, {115.59260559082031, 42.580599999999997}} + Class + ShapedGraphic + FontInfo + + Color + + a + 0.5 + b + 0 + g + 0 + r + 0 + + Font + Helvetica + Size + 12 + + ID + 205 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 Your game code} + + + + Class + LineGraphic + Head + + ID + 157 + Info + 8 + + ID + 204 + Points + + {212, 269.08485000000002} + {283.00000000000006, 269.08485000000002} + + Style + + stroke + + HeadArrow + FilledArrow + Legacy + + TailArrow + 0 + + + Tail + + ID + 203 + Info + 7 + + + + Bounds + {{43, 244}, {169, 50.169699999999999}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0 + r + 0 + + Font + DroidSans-Bold + Size + 10 + + ID + 203 + Magnets + + {1, 1} + {1, -1} + {-1, -1} + {-1, 1} + {0, 1} + {0, -1} + {1, 0} + {-1, 0} + {-0.5, -0.233518} + {-0.49144199999999999, 0.26006299999999999} + {0.50711799999999996, -0.22408600000000001} + {0.50711799999999996, 0.267179} + {-0.27431, -0.474028} + {0.27977999999999997, -0.47847800000000001} + {0.29393799999999998, 0.54304399999999997} + {-0.28623199999999999, 0.55380399999999996} + + Shape + Rectangle + Style + + fill + + Color + + b + 0 + g + 0.5 + r + 1 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.2 + g + 0.4 + r + 0.6 + + + shadow + + Color + + a + 0.35 + b + 0 + g + 0 + r + 0 + + Fuzziness + 2.3972222805023193 + ShadowVector + {0, 1} + + stroke + + Color + + b + 0.2 + g + 0.4 + r + 0.6 + + CornerRadius + 3 + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 GameView} + VerticalPad + 0 + + + + Bounds + {{519.5, 399}, {162, 42.580599999999997}} + Class + ShapedGraphic + FontInfo + + Color + + a + 0.5 + b + 0 + g + 0 + r + 0 + + Font + Helvetica + Size + 12 + + ID + 202 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 Implementation on Android 2.3 up to Android 4.0} + + + + Bounds + {{526.5, 108.4194}, {148, 42.580599999999997}} + Class + ShapedGraphic + FontInfo + + Color + + a + 0.5 + b + 0 + g + 0 + r + 0 + + Font + Helvetica + Size + 12 + + ID + 201 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 "Proxy" implementation on Android 4.1 and higher} + + + + Bounds + {{327.40739440917969, 209}, {80.185199999999995, 42.580599999999997}} + Class + ShapedGraphic + FontInfo + + Color + + a + 0.5 + b + 0 + g + 0 + r + 0 + + Font + Helvetica + Size + 12 + + ID + 100 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 Interface} + + + + Class + LineGraphic + FontInfo + + Font + DroidSans + Size + 11 + + Head + + ID + 157 + Info + 7 + + ID + 200 + OrthogonalBarAutomatic + + OrthogonalBarPoint + {0, 0} + OrthogonalBarPosition + 20.600000381469727 + Points + + {540, 368.91515000000004} + {475, 283} + {451.99999999999994, 269.08485000000002} + + Style + + stroke + + Color + + a + 0.7 + b + 0 + g + 0 + r + 0 + + CornerRadius + 4 + HeadArrow + FilledArrow + Legacy + + LineType + 2 + TailArrow + FilledArrow + + + Tail + + ID + 159 + Info + 8 + + + + Class + LineGraphic + FontInfo + + Font + DroidSans + Size + 11 + + Head + + ID + 157 + Info + 7 + + ID + 198 + OrthogonalBarAutomatic + + OrthogonalBarPoint + {0, 0} + OrthogonalBarPosition + 20.657249450683594 + Points + + {540, 176.08484999999999} + {474.88549999999998, 177.5} + {451.99999999999994, 269.08485000000002} + + Style + + stroke + + Color + + a + 0.7 + b + 0 + g + 0 + r + 0 + + CornerRadius + 4 + HeadArrow + FilledArrow + Legacy + + LineType + 2 + TailArrow + FilledArrow + + + Tail + + ID + 158 + Info + 8 + + + + Bounds + {{540, 343.83030000000002}, {121, 50.169699999999999}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0 + r + 0 + + Font + DroidSans-Bold + Size + 10 + + ID + 159 + Magnets + + {1, 1} + {1, -1} + {-1, -1} + {-1, 1} + {0, 1} + {0, -1} + {1, 0} + {-1, 0} + {-0.5, -0.233518} + {-0.49144199999999999, 0.26006299999999999} + {0.50711799999999996, -0.22408600000000001} + {0.50711799999999996, 0.267179} + {-0.27431, -0.474028} + {0.27977999999999997, -0.47847800000000001} + {0.29393799999999998, 0.54304399999999997} + {-0.28623199999999999, 0.55380399999999996} + + Shape + Rectangle + Style + + fill + + Color + + b + 1 + g + 0.874135 + r + 0.71718 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 1 + g + 0.662438 + r + 0.464468 + + + shadow + + Color + + a + 0.35 + b + 0 + g + 0 + r + 0 + + Fuzziness + 2.3972222805023193 + ShadowVector + {0, 1} + + stroke + + Color + + b + 0.93512 + g + 0.472602 + r + 0.333854 + + CornerRadius + 3 + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 InputManagerV9} + VerticalPad + 0 + + + + Bounds + {{540, 151}, {121, 50.169699999999999}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0 + r + 0 + + Font + DroidSans-Bold + Size + 10 + + ID + 158 + Magnets + + {1, 1} + {1, -1} + {-1, -1} + {-1, 1} + {0, 1} + {0, -1} + {1, 0} + {-1, 0} + {-0.5, -0.233518} + {-0.49144199999999999, 0.26006299999999999} + {0.50711799999999996, -0.22408600000000001} + {0.50711799999999996, 0.267179} + {-0.27431, -0.474028} + {0.27977999999999997, -0.47847800000000001} + {0.29393799999999998, 0.54304399999999997} + {-0.28623199999999999, 0.55380399999999996} + + Shape + Rectangle + Style + + fill + + Color + + b + 1 + g + 0.874135 + r + 0.71718 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 1 + g + 0.662438 + r + 0.464468 + + + shadow + + Color + + a + 0.35 + b + 0 + g + 0 + r + 0 + + Fuzziness + 2.3972222805023193 + ShadowVector + {0, 1} + + stroke + + Color + + b + 0.93512 + g + 0.472602 + r + 0.333854 + + CornerRadius + 3 + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 InputManagerV16} + VerticalPad + 0 + + + + Bounds + {{283, 244}, {169, 50.169699999999999}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0 + r + 0 + + Font + DroidSans-Bold + Size + 10 + + ID + 157 + Magnets + + {1, 1} + {1, -1} + {-1, -1} + {-1, 1} + {0, 1} + {0, -1} + {1, 0} + {-1, 0} + {-0.5, -0.233518} + {-0.49144199999999999, 0.26006299999999999} + {0.50711799999999996, -0.22408600000000001} + {0.50711799999999996, 0.267179} + {-0.27431, -0.474028} + {0.27977999999999997, -0.47847800000000001} + {0.29393799999999998, 0.54304399999999997} + {-0.28623199999999999, 0.55380399999999996} + + Shape + Rectangle + Style + + fill + + Color + + b + 1 + g + 0.874135 + r + 0.71718 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 1 + g + 0.662438 + r + 0.464468 + + + shadow + + Color + + a + 0.35 + b + 0 + g + 0 + r + 0 + + Fuzziness + 2.3972222805023193 + ShadowVector + {0, 1} + + stroke + + Color + + b + 0.93512 + g + 0.472602 + r + 0.333854 + + CornerRadius + 3 + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\b\fs24 \cf0 InputManagerCompat} + VerticalPad + 0 + + + + GridInfo + + GuidesLocked + NO + GuidesVisible + YES + HPages + 1 + ImageCounter + 1 + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + Animate + NO + circoMinDist + 18 + circoSeparation + 0.0 + layoutEngine + dot + neatoSeparation + 0.0 + twopiSeparation + 0.0 + + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + ModificationDate + 2014-01-17 19:18:34 +0000 + Modifier + Quddus Chong + NotesVisible + NO + Orientation + 2 + OriginVisible + NO + PageBreaks + YES + PrintInfo + + NSBottomMargin + + float + 41 + + NSHorizonalPagination + + coded + BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG + + NSLeftMargin + + float + 18 + + NSOrientation + + coded + BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwGG + + NSPaperSize + + size + {792, 612} + + NSPrintReverseOrientation + + int + 0 + + NSRightMargin + + float + 18 + + NSTopMargin + + float + 18 + + + PrintOnePage + + ReadOnly + NO + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 1 + SmartAlignmentGuidesActive + YES + SmartDistanceGuidesActive + YES + UniqueID + 1 + UseEntirePage + + VPages + 1 + WindowInfo + + CurrentSheet + 0 + ExpandedCanvases + + + name + Canvas 1 + + + Frame + {{130, 0}, {1112, 878}} + ListView + + OutlineWidth + 142 + RightSidebar + + ShowRuler + + Sidebar + + SidebarWidth + 120 + VisibleRegion + {{-111, -85}, {977, 723}} + Zoom + 1 + ZoomValues + + + Canvas 1 + 1 + 1 + + + + + diff --git a/docs/image_sources/training/game-controllers/game-controller-profiles.graffle b/docs/image_sources/training/game-controllers/game-controller-profiles.graffle new file mode 100644 index 0000000000000000000000000000000000000000..fc62fc595c8fc3591555f9a3ffc4daeb54b7c763 --- /dev/null +++ b/docs/image_sources/training/game-controllers/game-controller-profiles.graffle @@ -0,0 +1,2773 @@ + + + + + ActiveLayerIndex + 1 + ApplicationVersion + + com.omnigroup.OmniGrafflePro + 139.18.0.187838 + + AutoAdjust + + BackgroundGraphic + + Bounds + {{0, 0}, {1152, 733}} + Class + SolidGraphic + ID + 2 + Style + + shadow + + Draws + NO + + stroke + + Draws + NO + + + + BaseZoom + 0 + CanvasOrigin + {0, 0} + ColumnAlign + 1 + ColumnSpacing + 36 + CreationDate + 2013-03-04 20:35:59 +0000 + Creator + Dan Galpin + DisplayScale + 1 0/72 in = 1.0000 in + ExportShapes + + + InspectorGroup + 255 + ShapeImageRect + {{2, 2}, {22, 22}} + ShapeName + F1E2180B-1353-44D7-847D-FBDA9B734CEF-643-000123C8E8BEFFA9 + ShouldExport + YES + StrokePath + + elements + + + element + MOVETO + point + {0.5, 0.49999619000000001} + + + control1 + {0.48931121999999999, 0.25319671999999999} + control2 + {0.44103335999999999, 0.012794494999999999} + element + CURVETO + point + {0.35516356999999998, -0.17620087000000001} + + + control1 + {0.15901183999999999, -0.60793686000000002} + control2 + {-0.15901278999999999, -0.60793686000000002} + element + CURVETO + point + {-0.35516452999999998, -0.17620087000000001} + + + control1 + {-0.44103335999999999, 0.012794494999999999} + control2 + {-0.48931216999999999, 0.25319671999999999} + element + CURVETO + point + {-0.5, 0.49999619000000001} + + + element + CLOSE + + + element + MOVETO + point + {0.5, 0.49999619000000001} + + + + TextBounds + {{0, 0}, {1, 1}} + + + InspectorGroup + 255 + ShapeImageRect + {{2, 2}, {22, 22}} + ShapeName + 3183FE08-E827-4DFB-B09C-26BEBDBFE644-643-00011FC72A7BAF86 + ShouldExport + YES + StrokePath + + elements + + + element + MOVETO + point + {0.16853929000000001, -0.16853952} + + + element + LINETO + point + {0.16853929000000001, -0.5} + + + element + LINETO + point + {-0.16853940000000001, -0.5} + + + element + LINETO + point + {-0.16853940000000001, -0.16853952} + + + element + LINETO + point + {-0.5, -0.16853952} + + + element + LINETO + point + {-0.5, 0.16853929000000001} + + + element + LINETO + point + {-0.16853940000000001, 0.16853929000000001} + + + element + LINETO + point + {-0.16853940000000001, 0.5} + + + element + LINETO + point + {0.16853929000000001, 0.5} + + + element + LINETO + point + {0.16853929000000001, 0.16853929000000001} + + + element + LINETO + point + {0.5, 0.16853929000000001} + + + element + LINETO + point + {0.5, -0.16853952} + + + element + CLOSE + + + element + MOVETO + point + {0.16853929000000001, -0.16853952} + + + + TextBounds + {{0, 0}, {1, 1}} + + + InspectorGroup + 255 + ShapeImageRect + {{2, 2}, {22, 22}} + ShapeName + C23360B7-896D-4E43-821E-E438291A4B42-643-000123B31059F14D + ShouldExport + YES + StrokePath + + elements + + + element + MOVETO + point + {0.12745094000000001, 0} + + + element + LINETO + point + {0.5, 0} + + + element + LINETO + point + {0.5, -0.5} + + + element + LINETO + point + {-0.5, -0.5} + + + element + LINETO + point + {-0.5, 0} + + + element + LINETO + point + {-0.12745094000000001, 0} + + + element + LINETO + point + {-0.12745094000000001, 0.5} + + + element + LINETO + point + {0.12745094000000001, 0.5} + + + element + CLOSE + + + element + MOVETO + point + {0.12745094000000001, 0} + + + + TextBounds + {{0, 0}, {1, 1}} + + + InspectorGroup + 255 + ShapeImageRect + {{2, 2}, {22, 22}} + ShapeName + 47D48890-EFFB-40D4-9B16-B837B487F35B-643-000123949D248202 + ShouldExport + YES + StrokePath + + elements + + + element + MOVETO + point + {-0.45872511999999999, -0.5} + + + control1 + {-0.51545845999999995, -0.26532650000000002} + control2 + {-0.51370585000000002, 0.098152161000000002} + element + CURVETO + point + {-0.45346713, 0.32486773000000002} + + + control1 + {-0.39142346, 0.55837822000000004} + control2 + {-0.29083102999999999, 0.55837822000000004} + element + CURVETO + point + {-0.22878745, 0.32486773000000002} + + + control1 + {-0.22439509999999999, 0.30833673} + control2 + {-0.2203137, 0.29107856999999998} + element + CURVETO + point + {-0.21654329, 0.27319621999999999} + + + element + LINETO + point + {0, 0.27319621999999999} + + + element + LINETO + point + {0.21654325999999999, 0.27319621999999999} + + + control1 + {0.22031379000000001, 0.29107856999999998} + control2 + {0.22439516000000001, 0.30833673} + element + CURVETO + point + {0.22878741999999999, 0.32486773000000002} + + + control1 + {0.29083102999999999, 0.55837822000000004} + control2 + {0.39142357999999999, 0.55837822000000004} + element + CURVETO + point + {0.45346701, 0.32486773000000002} + + + control1 + {0.51370572999999997, 0.098152161000000002} + control2 + {0.51545845999999995, -0.26532650000000002} + element + CURVETO + point + {0.45872509, -0.5} + + + element + LINETO + point + {0.22352933999999999, -0.5} + + + control1 + {0.22344869000000001, -0.49966621} + control2 + {0.22336811000000001, -0.49933242999999999} + element + CURVETO + point + {0.22328770000000001, -0.49899817000000002} + + + element + LINETO + point + {0, -0.49899817000000002} + + + element + LINETO + point + {-0.22328772999999999, -0.49899817000000002} + + + control1 + {-0.22336813999999999, -0.49933242999999999} + control2 + {-0.22344869000000001, -0.49966621} + element + CURVETO + point + {-0.22352933999999999, -0.5} + + + element + CLOSE + + + element + MOVETO + point + {-0.45872511999999999, -0.5} + + + + TextBounds + {{0, 0}, {1, 1}} + + + InspectorGroup + 255 + ShapeImageRect + {{2, 2}, {22, 22}} + ShapeName + 1ECB1682-27C7-4AA3-B9D7-C873C7E35CC3-643-0001204E86C9927B + ShouldExport + YES + StrokePath + + elements + + + element + MOVETO + point + {0, -0.34815770000000001} + + + element + LINETO + point + {-0.17229146000000001, -0.34815770000000001} + + + control1 + {-0.17213887, -0.38709176000000001} + control2 + {-0.17783916, -0.43129521999999998} + element + CURVETO + point + {-0.19134480000000001, -0.45584047} + + + control1 + {-0.20903580999999999, -0.48799281999999999} + control2 + {-0.30287585, -0.52989668000000001} + element + CURVETO + point + {-0.36076200000000003, -0.46868926} + + + control1 + {-0.40301216000000001, -0.42401546000000001} + control2 + {-0.4273439, -0.36313765999999997} + element + CURVETO + point + {-0.44148481000000001, -0.33348655999999999} + + + control1 + {-0.45792991, -0.29900670000000001} + control2 + {-0.47685239000000001, -0.18920386} + element + CURVETO + point + {-0.47935599000000001, -0.17634570999999999} + + + control1 + {-0.50688135999999995, -0.035002232000000001} + control2 + {-0.50688135999999995, 0.19415568999999999} + element + CURVETO + point + {-0.47935599000000001, 0.33549797999999997} + + + control1 + {-0.47144359000000002, 0.37612997999999997} + control2 + {-0.46211770000000002, 0.40508091000000002} + element + CURVETO + point + {-0.45219076000000002, 0.42235231000000001} + + + control1 + {-0.45041117000000003, 0.42638290000000001} + control2 + {-0.44854629000000001, 0.43031728000000002} + element + CURVETO + point + {-0.44659655999999998, 0.43414568999999997} + + + control1 + {-0.40189775999999999, 0.52195132} + control2 + {-0.32942747999999999, 0.52195132} + element + CURVETO + point + {-0.28472847000000001, 0.43414568999999997} + + + control1 + {-0.252276, 0.37039875999999999} + control2 + {-0.24518039999999999, 0.31812059999999998} + element + CURVETO + point + {-0.24518039999999999, 0.31812059999999998} + + + control1 + {-0.23554211999999999, 0.26876652000000001} + control2 + {-0.23579177000000001, 0.25717378000000002} + element + CURVETO + point + {-0.22040003999999999, 0.28052354000000002} + + + control1 + {-0.17852514999999999, 0.34405743999999999} + control2 + {-0.11063104999999999, 0.34405743999999999} + element + CURVETO + point + {-0.068755567000000004, 0.28052354000000002} + + + control1 + {-0.053364097999999999, 0.25717378000000002} + control2 + {-0.043629705999999997, 0.22848975999999999} + element + CURVETO + point + {-0.039552628999999999, 0.19839119999999999} + + + element + LINETO + point + {0, 0.19839119999999999} + + + element + LINETO + point + {0.039552628999999999, 0.19839119999999999} + + + control1 + {0.043629705999999997, 0.22848975999999999} + control2 + {0.053364097999999999, 0.25717378000000002} + element + CURVETO + point + {0.068755567000000004, 0.28052354000000002} + + + control1 + {0.11063104999999999, 0.34405743999999999} + control2 + {0.17852509, 0.34405743999999999} + element + CURVETO + point + {0.22040008999999999, 0.28052354000000002} + + + control1 + {0.23579174, 0.25717378000000002} + control2 + {0.23554211999999999, 0.26876652000000001} + element + CURVETO + point + {0.24518037000000001, 0.31812059999999998} + + + control1 + {0.24518037000000001, 0.31812059999999998} + control2 + {0.252276, 0.37039875999999999} + element + CURVETO + point + {0.28472847000000001, 0.43414568999999997} + + + control1 + {0.32942747999999999, 0.52195132} + control2 + {0.40189778999999998, 0.52195132} + element + CURVETO + point + {0.44659662, 0.43414568999999997} + + + control1 + {0.44854629000000001, 0.43031728000000002} + control2 + {0.45041120000000001, 0.42638290000000001} + element + CURVETO + point + {0.45219076000000002, 0.42235231000000001} + + + control1 + {0.46211766999999998, 0.40508091000000002} + control2 + {0.47144364999999999, 0.37612997999999997} + element + CURVETO + point + {0.47935592999999999, 0.33549797999999997} + + + control1 + {0.50688135999999995, 0.19415568999999999} + control2 + {0.50688135999999995, -0.035002232000000001} + element + CURVETO + point + {0.47935592999999999, -0.17634570999999999} + + + control1 + {0.47685242, -0.18920386} + control2 + {0.45792985000000003, -0.29900670000000001} + element + CURVETO + point + {0.44148481000000001, -0.33348655999999999} + + + control1 + {0.42734396000000002, -0.36313765999999997} + control2 + {0.40301216000000001, -0.42401546000000001} + element + CURVETO + point + {0.36076200000000003, -0.46868926} + + + control1 + {0.30287587999999999, -0.52989668000000001} + control2 + {0.20903580999999999, -0.48799281999999999} + element + CURVETO + point + {0.19134480000000001, -0.45584047} + + + control1 + {0.17783916, -0.43129521999999998} + control2 + {0.17213881, -0.38709176000000001} + element + CURVETO + point + {0.17229140000000001, -0.34815770000000001} + + + element + CLOSE + + + element + MOVETO + point + {0, -0.34815770000000001} + + + + TextBounds + {{0, 0}, {1, 1}} + + + InspectorGroup + 255 + ShapeImageRect + {{2, 2}, {22, 22}} + ShapeName + A5792564-7BB8-4CC5-9068-338E366F4CDF-643-000123FA94AC0471 + ShouldExport + YES + StrokePath + + elements + + + element + MOVETO + point + {0.49999988000000001, 0.5} + + + element + LINETO + point + {0.49999988000000001, -0.49681281999999999} + + + element + LINETO + point + {0.41531157000000002, -0.49681281999999999} + + + control1 + {0.41504394999999999, -0.49787712000000001} + control2 + {0.41477596999999999, -0.49893760999999998} + element + CURVETO + point + {0.41450751000000002, -0.5} + + + element + LINETO + point + {-0.36829965999999997, -0.5} + + + control1 + {-0.44126809, -0.21159172000000001} + control2 + {-0.48516821999999998, 0.13798714000000001} + element + CURVETO + point + {-0.50000005999999997, 0.5} + + + element + CLOSE + + + element + MOVETO + point + {0.49999988000000001, 0.5} + + + + TextBounds + {{0, 0}, {1, 1}} + + + GraphDocumentVersion + 8 + GraphicsList + + + Class + Group + Graphics + + + Bounds + {{352.69015502929688, 157.75728808270409}, {39.000000000000007, 26.271913528442393}} + Class + ShapedGraphic + HFlip + YES + ID + 266 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 4} + + VFlip + YES + + + Bounds + {{368.23110857347933, 183.90486894506589}, {12.74318471799012, 11.874331160357771}} + Class + ShapedGraphic + HFlip + YES + ID + 267 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + VFlip + YES + + + HFlip + YES + ID + 265 + Layer + 1 + VFlip + YES + + + Class + Group + Graphics + + + Bounds + {{391.69015389164184, 123.94366571766557}, {39.000000000000007, 26.271913528442393}} + Class + ShapedGraphic + HFlip + YES + ID + 263 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 6} + + VFlip + YES + + + Bounds + {{407.2311074358243, 150.09124658002736}, {12.74318471799012, 11.874331160357771}} + Class + ShapedGraphic + HFlip + YES + ID + 264 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + VFlip + YES + + + HFlip + YES + ID + 262 + Layer + 1 + VFlip + YES + + + Class + Group + Graphics + + + Bounds + {{450.8726169276494, 218.13094531464776}, {39.000000000000007, 26.271913528442397}} + Class + ShapedGraphic + ID + 260 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 7} + + + + Bounds + {{461.58847866547882, 206.38094682037035}, {12.743184717990125, 11.874331160357764}} + Class + ShapedGraphic + ID + 261 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + + + ID + 259 + Layer + 1 + + + Class + Group + Graphics + + + Bounds + {{408.19367815071467, 250.48240007546275}, {39.000000000000007, 26.271913528442397}} + Class + ShapedGraphic + ID + 257 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 5} + + + + Bounds + {{418.90953988854409, 238.73240158118534}, {12.743184717990125, 11.874331160357764}} + Class + ShapedGraphic + ID + 258 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + + + ID + 256 + Layer + 1 + + + Class + Group + Graphics + + + Bounds + {{177.5007049887455, 155.402868593243}, {39, 26.271913528442383}} + Class + ShapedGraphic + ID + 248 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 1} + + VFlip + YES + + + Bounds + {{188.21656672657295, 181.55044945560479}, {12.743184717990118, 11.874331160357769}} + Class + ShapedGraphic + ID + 249 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + VFlip + YES + + + ID + 247 + Layer + 1 + VFlip + YES + + + Class + Group + Graphics + + + Bounds + {{1033.9784545898438, 270.88415416934021}, {39.000000000000007, 26.271913528442397}} + Class + ShapedGraphic + ID + 245 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 11} + + + + Bounds + {{1044.6943163276731, 259.1341556750628}, {12.743184717990125, 11.874331160357764}} + Class + ShapedGraphic + ID + 246 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + + + ID + 244 + Layer + 1 + + + Class + Group + Graphics + + + Bounds + {{963.48967113392712, 314.58124191062183}, {39.000000000000021, 26.271913528442411}} + Class + ShapedGraphic + HFlip + YES + ID + 242 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 10} + + + + Bounds + {{979.03062467810946, 302.83124341634465}, {12.743184717990129, 11.874331160357775}} + Class + ShapedGraphic + HFlip + YES + ID + 243 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + + + HFlip + YES + ID + 241 + Layer + 1 + + + Class + Group + Graphics + + + Bounds + {{692.25354204809798, 314.58123668887146}, {39.000000000000007, 26.271913528442397}} + Class + ShapedGraphic + ID + 53 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 9} + + + + Bounds + {{702.9694037859274, 302.83123819459405}, {12.743184717990125, 11.874331160357764}} + Class + ShapedGraphic + ID + 54 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + + + ID + 52 + Layer + 1 + + + Class + Group + Graphics + + + Bounds + {{629.80290222167969, 270.8841541693406}, {39.000000000000021, 26.271913528442411}} + Class + ShapedGraphic + HFlip + YES + ID + 233 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 8} + + + + Bounds + {{645.34385576586203, 259.13415567506343}, {12.743184717990129, 11.874331160357775}} + Class + ShapedGraphic + HFlip + YES + ID + 234 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + + + HFlip + YES + ID + 232 + Layer + 1 + + + Class + Group + Graphics + + + Bounds + {{307.48943270607771, 230.98592247383127}, {39.000000000000007, 26.271913528442393}} + Class + ShapedGraphic + HFlip + YES + ID + 224 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 3} + + VFlip + YES + + + Bounds + {{323.03038625026016, 257.13350333619303}, {12.74318471799012, 11.874331160357771}} + Class + ShapedGraphic + HFlip + YES + ID + 225 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + VFlip + YES + + + HFlip + YES + ID + 223 + Layer + 1 + VFlip + YES + + + Class + Group + Graphics + + + Bounds + {{219.01409112610222, 230.98592247383127}, {39, 26.271913528442383}} + Class + ShapedGraphic + ID + 44 + Shape + Rectangle + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + CornerRadius + 4 + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf1 2} + + VFlip + YES + + + Bounds + {{229.7299528639297, 257.13350333619303}, {12.743184717990118, 11.874331160357769}} + Class + ShapedGraphic + ID + 45 + Shape + Bezier + ShapeData + + UnitPoints + + {0.5, 0.5} + {0.5, 0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, -0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {-0.5, 0.5} + {0.5, 0.5} + + + Style + + fill + + Color + + b + 0.898039 + g + 0.709804 + r + 0.2 + + + shadow + + Fuzziness + 6.559971809387207 + ShadowVector + {1, 1} + + stroke + + Draws + NO + + + VFlip + YES + + + ID + 43 + Layer + 1 + VFlip + YES + + + Bounds + {{242.99996945271255, 45.774649604258862}, {93, 26}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + ID + 208 + Layer + 1 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 OpenSans;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 Front View } + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{809.73941206158747, 45.774649604259338}, {79, 26}} + Class + ShapedGraphic + FitText + YES + Flow + Resize + ID + 139 + Layer + 1 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 OpenSans;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 Top View } + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{978.23938675601653, 264.33125182518802}, {34, 38.5}} + Class + ShapedGraphic + ID + 130 + Layer + 1 + Shape + Rectangle + + + Bounds + {{687.23938675601585, 262.87654182518804}, {34, 38.5}} + Class + ShapedGraphic + ID + 1 + Layer + 1 + Shape + Rectangle + + + Bounds + {{942.59531675601613, 226.37654182518816}, {125.28812000000001, 30.5}} + Class + ShapedGraphic + HFlip + YES + ID + 129 + Layer + 1 + Shape + A5792564-7BB8-4CC5-9068-338E366F4CDF-643-000123FA94AC0471 + + + Bounds + {{630.95126275601592, 226.37654182518816}, {125.28812000000001, 30.5}} + Class + ShapedGraphic + ID + 128 + Layer + 1 + Shape + A5792564-7BB8-4CC5-9068-338E366F4CDF-643-000123FA94AC0471 + + + Bounds + {{879.23938675601585, 198.76546182518825}, {51, 17.611084000000002}} + Class + ShapedGraphic + ID + 118 + Layer + 1 + Shape + C23360B7-896D-4E43-821E-E438291A4B42-643-000123B31059F14D + + + Bounds + {{765.23938675601585, 198.76546182518825}, {51, 17.611084000000002}} + Class + ShapedGraphic + ID + 117 + Layer + 1 + Shape + C23360B7-896D-4E43-821E-E438291A4B42-643-000123B31059F14D + + + Bounds + {{641.73978375601587, 216.37654182518816}, {416.99918000000002, 97}} + Class + ShapedGraphic + ID + 113 + Layer + 1 + Shape + 47D48890-EFFB-40D4-9B16-B837B487F35B-643-000123949D248202 + + + Bounds + {{946.47890184399159, 205.37654838106059}, {87, 11}} + Class + ShapedGraphic + ID + 125 + Layer + 1 + Shape + Rectangle + + + Bounds + {{723.23945847751429, 205.37654182518818}, {24.522554, 11}} + Class + ShapedGraphic + ID + 124 + Layer + 1 + Shape + F1E2180B-1353-44D7-847D-FBDA9B734CEF-643-000123C8E8BEFFA9 + Style + + Text + + VerticalPad + 0 + + + + Bounds + {{667.73098603906249, 205.37654008460478}, {24.522554, 11}} + Class + ShapedGraphic + ID + 123 + Layer + 1 + Shape + F1E2180B-1353-44D7-847D-FBDA9B734CEF-643-000123C8E8BEFFA9 + Style + + Text + + VerticalPad + 0 + + + + Bounds + {{692.25351727169038, 203.33122213769531}, {28.981200999999999, 13.045318999999999}} + Class + ShapedGraphic + ID + 122 + Layer + 1 + Shape + F1E2180B-1353-44D7-847D-FBDA9B734CEF-643-000123C8E8BEFFA9 + Style + + Text + + VerticalPad + 0 + + + + Bounds + {{408.19366000000002, 212.95468}, {29.113309999999998, 28.843737000000001}} + Class + ShapedGraphic + FontInfo + + Font + Helvetica + Size + 12 + + ID + 69 + Layer + 2 + Shape + Circle + Style + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 OpenSans-Light;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 A} + VerticalPad + 0 + + + + Bounds + {{438.13702000000001, 185.56351000000001}, {29.113309999999998, 28.843737000000001}} + Class + ShapedGraphic + ID + 70 + Layer + 2 + Shape + Circle + Style + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 OpenSans;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 B} + VerticalPad + 0 + + + + Bounds + {{378.25031000000001, 185.56351000000001}, {29.113309999999998, 28.843737000000001}} + Class + ShapedGraphic + FontInfo + + Font + Helvetica + Size + 12 + + ID + 71 + Layer + 2 + Shape + Circle + Style + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 OpenSans;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 X} + VerticalPad + 0 + + + + Bounds + {{408.19366000000002, 157.75729000000001}, {29.113309999999998, 28.843737000000001}} + Class + ShapedGraphic + ID + 72 + Layer + 2 + Shape + Circle + Style + + Text + + Text + {\rtf1\ansi\ansicpg1252\cocoartf1265 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 OpenSans;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs24 \cf0 Y} + VerticalPad + 0 + + + + Bounds + {{320.50031000000001, 258.40285999999998}, {54, 53.5}} + Class + ShapedGraphic + ID + 73 + Layer + 2 + Shape + Circle + Style + + Text + + VerticalPad + 0 + + + + Bounds + {{108.50031, 155.40286}, {89, 89}} + Class + ShapedGraphic + ID + 74 + Layer + 2 + Shape + 3183FE08-E827-4DFB-B09C-26BEBDBFE644-643-00011FC72A7BAF86 + + + Bounds + {{201.00031000000001, 258.40285999999998}, {54, 53.5}} + Class + ShapedGraphic + ID + 75 + Layer + 2 + Shape + Circle + Style + + Text + + VerticalPad + 0 + + + + Bounds + {{82.500366, 105.36841}, {414.99921000000001, 273.53435999999999}} + Class + ShapedGraphic + ID + 76 + Layer + 2 + Shape + 1ECB1682-27C7-4AA3-B9D7-C873C7E35CC3-643-0001204E86C9927B + + + Bounds + {{359.61246, 96.000022999999999}, {112.00001, 48.902847000000001}} + Class + ShapedGraphic + HFlip + YES + ID + 77 + Layer + 2 + Shape + Bezier + ShapeData + + UnitPoints + + {0.49998891000000001, 0.34931803} + {0.50055384999999997, 0.13154410999999999} + {0.47943342, -0.11570501} + {0.42939042999999999, -0.25299692000000001} + {0.36383854999999998, -0.43283796000000002} + {0.016128421, -0.66722440999999999} + {-0.19835973000000001, -0.32486677000000003} + {-0.35491191999999999, -0.074985503999999994} + {-0.44506912999999998, 0.26552963000000002} + {-0.49746638999999998, 0.43138074999999998} + {-0.55840153000000003, 0.62424135000000003} + {0.49998891000000001, 0.34931803} + + + + + Bounds + {{104.50069000000001, 96.368431000000001}, {112.00001, 48.902847000000001}} + Class + ShapedGraphic + ID + 78 + Layer + 2 + Shape + Bezier + ShapeData + + UnitPoints + + {0.49998891000000001, 0.34931803} + {0.50055384999999997, 0.13154410999999999} + {0.47943342, -0.11570501} + {0.42939042999999999, -0.25299692000000001} + {0.36383854999999998, -0.43283796000000002} + {0.016128421, -0.66722440999999999} + {-0.19835973000000001, -0.32486677000000003} + {-0.35491191999999999, -0.074985503999999994} + {-0.44506912999999998, 0.26552963000000002} + {-0.49746638999999998, 0.43138074999999998} + {-0.55840153000000003, 0.62424135000000003} + {0.49998891000000001, 0.34931803} + + + + + GridInfo + + ShowsGrid + YES + + GuidesLocked + NO + GuidesVisible + YES + HPages + 2 + ImageCounter + 3 + KeepToScale + + Layers + + + Lock + NO + Name + Layer 3 + Print + YES + View + YES + + + Lock + NO + Name + Layer 2 + Print + YES + View + YES + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + Animate + NO + circoMinDist + 18 + circoSeparation + 0.0 + layoutEngine + dot + neatoSeparation + 0.0 + twopiSeparation + 0.0 + + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + ModificationDate + 2014-01-22 02:45:32 +0000 + Modifier + Quddus Chong + NotesVisible + NO + Orientation + 2 + OriginVisible + NO + PageBreaks + YES + PrintInfo + + NSBottomMargin + + float + 41 + + NSHorizonalPagination + + int + 0 + + NSLeftMargin + + float + 18 + + NSPaperSize + + size + {612, 792} + + NSPrintReverseOrientation + + int + 0 + + NSRightMargin + + float + 18 + + NSTopMargin + + float + 18 + + + PrintOnePage + + ReadOnly + NO + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 1 + SmartAlignmentGuidesActive + YES + SmartDistanceGuidesActive + YES + UniqueID + 1 + UseEntirePage + + VPages + 1 + WindowInfo + + CurrentSheet + 0 + ExpandedCanvases + + + name + Canvas 1 + + + Frame + {{4, 67}, {1436, 769}} + ListView + + OutlineWidth + 142 + RightSidebar + + ShowRuler + + Sidebar + + SidebarWidth + 120 + VisibleRegion + {{159.85915976085272, 0}, {991.54932574132454, 421.83099866410038}} + Zoom + 1.4199999570846558 + ZoomValues + + + Canvas 1 + 1.4199999570846558 + 0.70999997854232788 + + + + + diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index efce606e5000cd724bdf54772d46ef8d2409259b..b8911d4d6d8c408ec3ff6099e44d62a06a6e4756 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -67,6 +67,7 @@ public final class Bitmap implements Parcelable { * setPremultiplied() aren't order dependent, despite being setters. */ private boolean mIsPremultiplied; + private byte[] mNinePatchChunk; // may be null private int[] mLayoutBounds; // may be null private int mWidth; @@ -390,7 +391,7 @@ public final class Bitmap implements Parcelable { * No color information is stored. * With this configuration, each pixel requires 1 byte of memory. */ - ALPHA_8 (2), + ALPHA_8 (1), /** * Each pixel is stored on 2 bytes and only the RGB channels are @@ -406,7 +407,7 @@ public final class Bitmap implements Parcelable { * This configuration may be useful when using opaque bitmaps * that do not require high color fidelity. */ - RGB_565 (4), + RGB_565 (3), /** * Each pixel is stored on 2 bytes. The three RGB color channels @@ -428,7 +429,7 @@ public final class Bitmap implements Parcelable { * it is advised to use {@link #ARGB_8888} instead. */ @Deprecated - ARGB_4444 (5), + ARGB_4444 (4), /** * Each pixel is stored on 4 bytes. Each channel (RGB and alpha @@ -438,13 +439,13 @@ public final class Bitmap implements Parcelable { * This configuration is very flexible and offers the best * quality. It should be used whenever possible. */ - ARGB_8888 (6); + ARGB_8888 (5); final int nativeInt; @SuppressWarnings({"deprecation"}) private static Config sConfigs[] = { - null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 + null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 }; Config(int ni) { @@ -554,7 +555,7 @@ public final class Bitmap implements Parcelable { checkRecycled("Can't copy a recycled bitmap"); Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable); if (b != null) { - b.mIsPremultiplied = mIsPremultiplied; + b.setAlphaAndPremultiplied(hasAlpha(), mIsPremultiplied); b.mDensity = mDensity; } return b; @@ -727,12 +728,12 @@ public final class Bitmap implements Parcelable { paint.setAntiAlias(true); } } - + // The new bitmap was created from a known bitmap source so assume that // they use the same density bitmap.mDensity = source.mDensity; - bitmap.mIsPremultiplied = source.mIsPremultiplied; - + bitmap.setAlphaAndPremultiplied(source.hasAlpha(), source.mIsPremultiplied); + canvas.setBitmap(bitmap); canvas.drawBitmap(source, srcR, dstR, paint); canvas.setBitmap(null); @@ -810,9 +811,9 @@ public final class Bitmap implements Parcelable { if (display != null) { bm.mDensity = display.densityDpi; } + bm.setHasAlpha(hasAlpha); if (config == Config.ARGB_8888 && !hasAlpha) { nativeErase(bm.mNativeBitmap, 0xff000000); - nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha); } // No need to initialize the bitmap to zeroes with other configs; // it is backed by a VM byte array which is by definition preinitialized @@ -1041,11 +1042,23 @@ public final class Bitmap implements Parcelable { *

    This method will not affect the behavior of a bitmap without an alpha * channel, or if {@link #hasAlpha()} returns false.

    * + *

    Calling createBitmap() or createScaledBitmap() with a source + * Bitmap whose colors are not pre-multiplied may result in a RuntimeException, + * since those functions require drawing the source, which is not supported for + * un-pre-multiplied Bitmaps.

    + * * @see Bitmap#isPremultiplied() * @see BitmapFactory.Options#inPremultiplied */ public final void setPremultiplied(boolean premultiplied) { mIsPremultiplied = premultiplied; + nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha(), premultiplied); + } + + /** Helper function to set both alpha and premultiplied. **/ + private final void setAlphaAndPremultiplied(boolean hasAlpha, boolean premultiplied) { + mIsPremultiplied = premultiplied; + nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, premultiplied); } /** Returns the bitmap's width */ @@ -1206,7 +1219,7 @@ public final class Bitmap implements Parcelable { * non-opaque per-pixel alpha values. */ public void setHasAlpha(boolean hasAlpha) { - nativeSetHasAlpha(mNativeBitmap, hasAlpha); + nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, mIsPremultiplied); } /** @@ -1611,7 +1624,8 @@ public final class Bitmap implements Parcelable { private static native void nativePrepareToDraw(long nativeBitmap); private static native boolean nativeHasAlpha(long nativeBitmap); - private static native void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha); + private static native void nativeSetAlphaAndPremultiplied(long nativeBitmap, boolean hasAlpha, + boolean isPremul); private static native boolean nativeHasMipMap(long nativeBitmap); private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap); private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1); diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 1f35e500d718e81044e27be0e9d775086c0a4268..67e8f23444acad5dd6daf236deba5004c9a976f5 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -153,8 +153,12 @@ public class BitmapFactory { * *

    This does not affect bitmaps without an alpha channel.

    * + *

    Setting this flag to false while setting {@link #inScaled} to true + * may result in incorrect colors.

    + * * @see Bitmap#hasAlpha() * @see Bitmap#isPremultiplied() + * @see #inScaled */ public boolean inPremultiplied; @@ -249,6 +253,9 @@ public class BitmapFactory { *

    This flag is turned on by default and should be turned off if you need * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this * flag and are always scaled. + * + *

    If {@link #inPremultiplied} is set to false, and the image has alpha, + * setting this flag to true may result in incorrect colors. */ public boolean inScaled; diff --git a/graphics/java/android/graphics/ColorMatrix.java b/graphics/java/android/graphics/ColorMatrix.java index c22cda1cbf511db575eb56f3fee87ff2fb96cce6..e3596c80808d5af57190e227f130ca50ef8f27dd 100644 --- a/graphics/java/android/graphics/ColorMatrix.java +++ b/graphics/java/android/graphics/ColorMatrix.java @@ -19,7 +19,7 @@ package android.graphics; import android.util.FloatMath; /** - * 5x4 matrix for transforming the color+alpha components of a Bitmap. + * 4x5 matrix for transforming the color+alpha components of a Bitmap. * The matrix is stored in a single array, and its treated as follows: * [ a, b, c, d, e, * f, g, h, i, j, diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 7814a01ad9295c5854349f2227f00fe851bc7028..58f5325476ebb3b46ffe236abfaa00ba20d05ff1 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -163,7 +163,7 @@ GLuint Program::buildShader(const char* source, GLenum type) { void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, const mat4& transformMatrix, bool offset) { - if (projectionMatrix != mProjection) { + if (projectionMatrix != mProjection || offset != mOffset) { if (CC_LIKELY(!offset)) { glUniformMatrix4fv(projection, 1, GL_FALSE, &projectionMatrix.data[0]); } else { @@ -177,6 +177,7 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]); } mProjection = projectionMatrix; + mOffset = offset; } mat4 t(transformMatrix); diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 4f94afce9164e5418029203b8771b487bbf862c6..f6ac8ec76da8013b2e580bd72f9c3c29cb2a493d 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -431,6 +431,7 @@ private: bool mHasSampler; mat4 mProjection; + bool mOffset; }; // class Program }; // namespace uirenderer diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 54a206bb2955344005d12d24935b1c54ba423a43..d5ba8c3008d51acb712e8b9a07f72ea45e127fa3 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -184,7 +184,7 @@ void TextureCache::clearGarbage() { Mutex::Autolock _l(mLock); size_t count = mGarbage.size(); for (size_t i = 0; i < count; i++) { - const SkBitmap* bitmap = mGarbage.itemAt(i); + SkBitmap* bitmap = mGarbage.itemAt(i); mCache.remove(bitmap); delete bitmap; } @@ -287,10 +287,9 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height) { SkBitmap rgbaBitmap; - rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType()); rgbaBitmap.allocPixels(); rgbaBitmap.eraseColor(0); - rgbaBitmap.setIsOpaque(bitmap->isOpaque()); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 8f5beb8927325a7b5421f437a81e4323f2a52ae7..436dcef39847b6a0d93ee76e24e280a2a7d31e0d 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include "FontUtil.h" @@ -271,9 +272,9 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre if (cachedGlyph) { // Is the glyph still in texture cache? if (!cachedGlyph->mIsValid) { - const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, - &mDescription.mLookupTransform); - updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching); + SkAutoGlyphCache autoCache(*paint, NULL, &mDescription.mLookupTransform); + const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit); + updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching); } } else { cachedGlyph = cacheGlyph(paint, textUnit, precaching); @@ -415,8 +416,8 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len } } -void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, - bool precaching) { +void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCache* skiaGlyphCache, + CachedGlyphInfo* glyph, bool precaching) { glyph->mAdvanceX = skiaGlyph.fAdvanceX; glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; @@ -429,7 +430,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp // Get the bitmap for the glyph if (!skiaGlyph.fImage) { - paint->findImage(skiaGlyph, &mDescription.mLookupTransform); + skiaGlyphCache->findImage(skiaGlyph); } mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching); @@ -463,11 +464,12 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); - const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform); + SkAutoGlyphCache autoCache(*paint, NULL, &mDescription.mLookupTransform); + const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), glyph); newGlyph->mIsValid = false; newGlyph->mGlyphIndex = skiaGlyph.fID; - updateGlyphCache(paint, skiaGlyph, newGlyph, precaching); + updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), newGlyph, precaching); return newGlyph; } diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 9e7ec2dcfe4d8f455182a264dbb6586fe652ca7f..f68b4308418743ddaeed2631d88a7ed3ca9de756 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -19,6 +19,7 @@ #include +#include #include #include #include @@ -117,8 +118,8 @@ private: void invalidateTextureCache(CacheTexture* cacheTexture = NULL); CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching); - void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, - bool precaching); + void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, SkGlyphCache* skiaGlyphCache, + CachedGlyphInfo* glyph, bool precaching); void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h index cdcb23c488e758c0bc64d66543d2d4ca34f164bd..c2fd5f58e02d4f0ff0d2f3cfb36ac33a557f08f4 100644 --- a/libs/hwui/font/FontUtil.h +++ b/libs/hwui/font/FontUtil.h @@ -40,7 +40,7 @@ #if RENDER_TEXT_AS_GLYPHS typedef uint16_t glyph_t; #define TO_GLYPH(g) g - #define GET_METRICS(paint, glyph, matrix) paint->getGlyphMetrics(glyph, matrix) + #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph) #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text) #define IS_END_OF_STRING(glyph) false @@ -53,7 +53,7 @@ #else typedef SkUnichar glyph_t; #define TO_GLYPH(g) ((SkUnichar) g) - #define GET_METRICS(paint, glyph, matrix) paint->getUnicharMetrics(glyph, matrix) + #define GET_METRICS(cache, glyph) cache->getUnicharMetrics(glyph) #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text) #define IS_END_OF_STRING(glyph) glyph < 0 #endif diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index b836f50b6b5244c083c343e551dbc426a7a8da32..92474df7a603a41acf0fdff5194e494f1d5dfe4c 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -49,6 +49,8 @@ import android.database.ContentObserver; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.net.Uri; +import android.net.http.CertificateChainValidator; +import android.net.http.SslError; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -81,10 +83,12 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; @@ -116,6 +120,8 @@ public class AudioService extends IAudioService.Stub { protected static final boolean DEBUG_RC = false; /** Debug volumes */ protected static final boolean DEBUG_VOL = false; + /** Debug cert verification */ + private static final boolean DEBUG_CERTS = false; /** How long to delay before persisting a change in volume/ringer mode. */ private static final int PERSIST_DELAY = 500; @@ -1019,7 +1025,7 @@ public class AudioService extends IAudioService.Stub { (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) { synchronized (mA2dpAvrcpLock) { if (mA2dp != null && mAvrcpAbsVolSupported) { - mA2dp.setAvrcpAbsoluteVolume(index); + mA2dp.setAvrcpAbsoluteVolume(index / 10); } } } @@ -4581,6 +4587,43 @@ public class AudioService extends IAudioService.Stub { } } + public int verifyX509CertChain(int numcerts, byte [] chain, String domain, String authType) { + + if (DEBUG_CERTS) { + Log.v(TAG, "java side verify for " + + numcerts + " certificates (" + chain.length + " bytes" + + ")for "+ domain + "/" + authType); + } + + byte[][] certChain = new byte[numcerts][]; + + ByteBuffer buf = ByteBuffer.wrap(chain); + for (int i = 0; i < numcerts; i++) { + int certlen = buf.getInt(); + if (DEBUG_CERTS) { + Log.i(TAG, "cert " + i +": " + certlen); + } + certChain[i] = new byte[certlen]; + buf.get(certChain[i]); + } + + try { + SslError err = CertificateChainValidator.verifyServerCertificates(certChain, + domain, authType); + if (DEBUG_CERTS) { + Log.i(TAG, "verified: " + err); + } + if (err == null) { + return -1; + } else { + return err.getPrimaryError(); + } + } catch (Exception e) { + Log.e(TAG, "failed to verify chain: " + e); + } + return SslError.SSL_INVALID; + } + //========================================================================================== // Camera shutter sound policy. diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 20eb35694146a1a4256d26947f2c4755825f2cf5..9db35fce7db7ad47acf70b3804fc9c87aa82d736 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -98,7 +98,7 @@ public class ExifInterface { private static SimpleDateFormat sFormatter; static { - System.loadLibrary("exif_jni"); + System.loadLibrary("jhead_jni"); sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); sFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 2f083256655d8add9e3a34a3412c7bfddc7e4f48..b5c3631398aa1c6d1ede8163095a64ca8c07e211 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -35,6 +35,8 @@ import android.view.KeyEvent; */ interface IAudioService { + int verifyX509CertChain(int chainsize, in byte[] chain, String host, String authtype); + void adjustVolume(int direction, int flags, String callingPackage); boolean isLocalOrRemoteMusicActive(); @@ -236,4 +238,5 @@ interface IAudioService { AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); boolean isCameraSoundForced(); + } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index b34cea8f459cff50a859bcdef08595e98847078e..41ba5d6429b8fcc9483f4cac448d99669c5e39c7 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -3128,7 +3128,7 @@ public class MediaPlayer implements SubtitleController.Listener if (refreshTime || nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) { try { - mLastTimeUs = mPlayer.getCurrentPosition() * 1000; + mLastTimeUs = mPlayer.getCurrentPosition() * 1000L; mPaused = !mPlayer.isPlaying(); if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs); } catch (IllegalStateException e) { diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index d17e8f8ba6bcb5958a3855f0e83e6cbf8a26afb4..a4d491d86b688d1bcb9134da14a8f1f3e2cfb083 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -60,9 +60,6 @@ public class MediaRouter { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static class Static implements DisplayManager.DisplayListener { - // Time between wifi display scans when actively scanning in milliseconds. - private static final int WIFI_DISPLAY_SCAN_INTERVAL = 10000; - final Context mAppContext; final Resources mResources; final IAudioService mAudioService; @@ -86,6 +83,7 @@ public class MediaRouter { final boolean mCanConfigureWifiDisplays; boolean mActivelyScanningWifiDisplays; + String mPreviousActiveWifiDisplayAddress; int mDiscoveryRequestRouteTypes; boolean mDiscoverRequestActiveScan; @@ -105,16 +103,6 @@ public class MediaRouter { } }; - final Runnable mScanWifiDisplays = new Runnable() { - @Override - public void run() { - if (mActivelyScanningWifiDisplays) { - mDisplayService.scanWifiDisplays(); - mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL); - } - } - }; - Static(Context appContext) { mAppContext = appContext; mResources = Resources.getSystem(); @@ -278,15 +266,24 @@ public class MediaRouter { } // Update wifi display scanning. - if (activeScanWifiDisplay && mCanConfigureWifiDisplays) { - if (!mActivelyScanningWifiDisplays) { - mActivelyScanningWifiDisplays = true; - mHandler.post(mScanWifiDisplays); + // TODO: All of this should be managed by the media router service. + if (mCanConfigureWifiDisplays) { + if (mSelectedRoute != null + && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) { + // Don't scan while already connected to a remote display since + // it may interfere with the ongoing transmission. + activeScanWifiDisplay = false; } - } else { - if (mActivelyScanningWifiDisplays) { - mActivelyScanningWifiDisplays = false; - mHandler.removeCallbacks(mScanWifiDisplays); + if (activeScanWifiDisplay) { + if (!mActivelyScanningWifiDisplays) { + mActivelyScanningWifiDisplays = true; + mDisplayService.startWifiDisplayScan(); + } + } else { + if (mActivelyScanningWifiDisplays) { + mActivelyScanningWifiDisplays = false; + mDisplayService.stopWifiDisplayScan(); + } } } @@ -944,6 +941,9 @@ public class MediaRouter { } dispatchRouteSelected(types & route.getSupportedTypes(), route); } + + // The behavior of active scans may depend on the currently selected route. + sStatic.updateDiscoveryRequest(); } static void selectDefaultRouteStatic() { @@ -1290,10 +1290,8 @@ public class MediaRouter { } static void updateWifiDisplayStatus(WifiDisplayStatus status) { - boolean wantScan = false; WifiDisplay[] displays; WifiDisplay activeDisplay; - if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) { displays = status.getDisplays(); activeDisplay = status.getActiveDisplay(); @@ -1313,6 +1311,8 @@ public class MediaRouter { displays = WifiDisplay.EMPTY_ARRAY; activeDisplay = null; } + String activeDisplayAddress = activeDisplay != null ? + activeDisplay.getDeviceAddress() : null; // Add or update routes. for (int i = 0; i < displays.length; i++) { @@ -1322,9 +1322,11 @@ public class MediaRouter { if (route == null) { route = makeWifiDisplayRoute(d, status); addRouteStatic(route); - wantScan = true; } else { - updateWifiDisplayRoute(route, d, status); + String address = d.getDeviceAddress(); + boolean disconnected = !address.equals(activeDisplayAddress) + && address.equals(sStatic.mPreviousActiveWifiDisplayAddress); + updateWifiDisplayRoute(route, d, status, disconnected); } if (d.equals(activeDisplay)) { selectRouteStatic(route.getSupportedTypes(), route, false); @@ -1343,11 +1345,9 @@ public class MediaRouter { } } - // Don't scan if we're already connected to a wifi display, - // the scanning process can cause a hiccup with some configurations. - if (wantScan && activeDisplay != null && sStatic.mCanConfigureWifiDisplays) { - sStatic.mDisplayService.scanWifiDisplays(); - } + // Remember the current active wifi display address so that we can infer disconnections. + // TODO: This hack will go away once all of this is moved into the media router service. + sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress; } private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) { @@ -1405,7 +1405,8 @@ public class MediaRouter { } private static void updateWifiDisplayRoute( - RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus) { + RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus, + boolean disconnected) { boolean changed = false; final String newName = display.getFriendlyDisplayName(); if (!route.getName().equals(newName)) { @@ -1423,7 +1424,7 @@ public class MediaRouter { dispatchRouteChanged(route); } - if (!enabled && route.isSelected()) { + if ((!enabled || disconnected) && route.isSelected()) { // Oops, no longer available. Reselect the default. selectDefaultRouteStatic(); } diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index 6dbb3cd1da612e6c0dc43509e0ebd085cba439c6..910b24c20637ebfe52438c1b78f289f2ba9a1774 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -404,7 +404,7 @@ public final class RemoteController * @throws IllegalArgumentException */ public boolean setSynchronizationMode(int sync) throws IllegalArgumentException { - if ((sync != POSITION_SYNCHRONIZATION_NONE) || (sync != POSITION_SYNCHRONIZATION_CHECK)) { + if ((sync != POSITION_SYNCHRONIZATION_NONE) && (sync != POSITION_SYNCHRONIZATION_CHECK)) { throw new IllegalArgumentException("Unknown synchronization mode " + sync); } if (!mIsRegistered) { diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 63a61e2edf229feb41562e85b582f6f6e1775130..dea971e4cfbcc00901eab4e051d60a2d9b9ff807 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -41,13 +41,13 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_amrnb_common \ LOCAL_REQUIRED_MODULES := \ - libexif_jni + libjhead_jni LOCAL_STATIC_LIBRARIES := \ libstagefright_amrnbenc LOCAL_C_INCLUDES += \ - external/jhead \ + external/libexif/ \ external/tremor/Tremor \ frameworks/base/core/jni \ frameworks/av/media/libmedia \ diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 8129c0dfe71193cc4c10349fc53afe74059a7578..7df56f4970680f74e782788e86e79d16a2647c14 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -38,7 +38,10 @@ #include "mtp.h" extern "C" { -#include "jhead.h" +#include "libexif/exif-content.h" +#include "libexif/exif-data.h" +#include "libexif/exif-tag.h" +#include "libexif/exif-utils.h" } using namespace android; @@ -751,6 +754,22 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, return result; } +static void foreachentry(ExifEntry *entry, void *user) { + char buf[1024]; + ALOGI("entry %x, format %d, size %d: %s", + entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf))); +} + +static void foreachcontent(ExifContent *content, void *user) { + ALOGI("content %d", exif_content_get_ifd(content)); + exif_content_foreach_entry(content, foreachentry, user); +} + +static long getLongFromExifEntry(ExifEntry *e) { + ExifByteOrder o = exif_data_get_byte_order(e->parent->parent); + return exif_get_long(e->data, o); +} + MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) { char date[20]; @@ -793,23 +812,22 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, // read EXIF data for thumbnail information if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) { - ResetJpgfile(); - // Start with an empty image information structure. - memset(&ImageInfo, 0, sizeof(ImageInfo)); - ImageInfo.FlashUsed = -1; - ImageInfo.MeteringMode = -1; - ImageInfo.Whitebalance = -1; - strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX); - if (ReadJpegFile((const char*)path, READ_METADATA)) { - Section_t* section = FindSection(M_EXIF); - if (section) { - info.mThumbCompressedSize = ImageInfo.ThumbnailSize; - info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; - info.mImagePixWidth = ImageInfo.Width; - info.mImagePixHeight = ImageInfo.Height; - } + + ExifData *exifdata = exif_data_new_from_file(path); + if (exifdata) { + //exif_data_foreach_content(exifdata, foreachcontent, NULL); + + // XXX get this from exif, or parse jpeg header instead? + ExifEntry *w = exif_content_get_entry( + exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION); + ExifEntry *h = exif_content_get_entry( + exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION); + info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0; + info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; + info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0; + info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0; + exif_data_unref(exifdata); } - DiscardData(); } checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -825,22 +843,16 @@ void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) { - ResetJpgfile(); - // Start with an empty image information structure. - memset(&ImageInfo, 0, sizeof(ImageInfo)); - ImageInfo.FlashUsed = -1; - ImageInfo.MeteringMode = -1; - ImageInfo.Whitebalance = -1; - strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX); - if (ReadJpegFile((const char*)path, READ_METADATA)) { - Section_t* section = FindSection(M_EXIF); - if (section) { - outThumbSize = ImageInfo.ThumbnailSize; - result = malloc(outThumbSize); - if (result) - memcpy(result, section->Data + ImageInfo.ThumbnailOffset + 8, outThumbSize); + + ExifData *exifdata = exif_data_new_from_file(path); + if (exifdata) { + if (exifdata->data) { + result = malloc(exifdata->size); + if (result) { + memcpy(result, exifdata->data, exifdata->size); + } } - DiscardData(); + exif_data_unref(exifdata); } } diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp index 6424744a43316dae34a82f11be43fbaa4ba93734..53f04bc6079c9517bbda0d98dffaf8b7c4a19d3d 100644 --- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp +++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "omx_jpeg_decoder.h" @@ -184,8 +185,7 @@ void OmxJpegImageDecoder::installPixelRef(MediaBuffer *buffer, sp d void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkBitmap::Config pref, int width, int height) { - bm->setConfig(getColorSpaceConfig(pref), width, height); - bm->setIsOpaque(true); + bm->setConfig(getColorSpaceConfig(pref), width, height, 0, kOpaque_SkAlphaType); } SkBitmap::Config OmxJpegImageDecoder::getColorSpaceConfig( diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java index 55d73f233608430fe2de8ccb42996f9182c5a552..1cbc221e1057d52ff738cb50c6c8dbcbe092ab4f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java @@ -51,7 +51,7 @@ public class FilteringCursorWrapper extends AbstractCursor { mPosition = new int[count]; cursor.moveToPosition(-1); - while (cursor.moveToNext()) { + while (cursor.moveToNext() && mCount < count) { final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java index 3a8a3fbdd10341be247325cff0f0cfa4576afaaf..34ce42dc56f4f7126b261b687312d95f91816e3e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -55,6 +55,10 @@ import java.util.concurrent.TimeUnit; public class RecentLoader extends AsyncTaskLoader { private static final boolean LOGD = true; + // TODO: clean up cursor ownership so background thread doesn't traverse + // previously returned cursors for filtering/sorting; this currently races + // with the UI thread. + private static final int MAX_OUTSTANDING_RECENTS = 4; private static final int MAX_OUTSTANDING_RECENTS_SVELTE = 2; diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index 1bae9b83e9daf7ae8cbbed4215b2330e9155ccd9..247089f5bad1359410edb0ebae2a51f73eb4b444 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -1655,6 +1655,7 @@ public class KeyguardHostView extends KeyguardViewBase { KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i); frame.removeAllViews(); } + mSecurityViewContainer.onPause(); // clean up any actions in progress } /** diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index e7f12599e7abb6be725d17e5eb42ecf56e71ea3e..3b72f4327e3bed3df08e9ed40a56207c9bcd8320 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -58,8 +58,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit // how many cells the user has to cross before we poke the wakelock private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2; - private int mFailedPatternAttemptsSinceLastTimeout = 0; - private int mTotalFailedPatternAttempts = 0; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private CountDownTimer mCountdownTimer = null; private LockPatternUtils mLockPatternUtils; private LockPatternView mLockPatternView; @@ -100,6 +100,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit public KeyguardPatternView(Context context, AttributeSet attrs) { super(context, attrs); + mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); } public void setKeyguardCallback(KeyguardSecurityCallback callback) { @@ -202,7 +203,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit if (mCallback.isVerifyUnlockOnly()) { updateFooter(FooterMode.VerifyUnlocked); } else if (mEnableFallback && - (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { + (mKeyguardUpdateMonitor.getFailedUnlockAttempts() + >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { updateFooter(FooterMode.ForgotLockPattern); } else { updateFooter(FooterMode.Normal); @@ -211,7 +213,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } private void displayDefaultSecurityMessage() { - if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) { + if (mKeyguardUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) { mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true); } else { mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false); @@ -262,20 +264,20 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit if (mLockPatternUtils.checkPattern(pattern)) { mCallback.reportSuccessfulUnlockAttempt(); mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); - mTotalFailedPatternAttempts = 0; mCallback.dismiss(true); } else { if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS); } mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); - if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { - mTotalFailedPatternAttempts++; - mFailedPatternAttemptsSinceLastTimeout++; + boolean registeredAttempt = + pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL; + if (registeredAttempt) { mCallback.reportFailedUnlockAttempt(); } - if (mFailedPatternAttemptsSinceLastTimeout - >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { + int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts(); + if (registeredAttempt && + 0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); handleAttemptLockout(deadline); } else { @@ -363,7 +365,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView.setEnabled(true); displayDefaultSecurityMessage(); // TODO mUnlockIcon.setVisibility(View.VISIBLE); - mFailedPatternAttemptsSinceLastTimeout = 0; if (mEnableFallback) { updateFooter(FooterMode.ForgotLockPattern); } else { diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 88403a390629fd92ac91d28a10906b9e934628b8..f6008d4c5b6d1618fc4e658b08bfe242cb8935f9 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -1529,9 +1529,13 @@ public class PrintJobConfigActivity extends Activity { builder.append(','); } PageRange pageRange = pageRanges[i]; - builder.append(pageRange.getStart()); - builder.append('-'); - builder.append(pageRange.getEnd()); + final int shownStartPage = pageRange.getStart() + 1; + final int shownEndPage = pageRange.getEnd() + 1; + builder.append(shownStartPage); + if (shownStartPage != shownEndPage) { + builder.append('-'); + builder.append(shownEndPage); + } } mPageRangeEditText.setText(builder.toString()); } @@ -2186,12 +2190,16 @@ public class PrintJobConfigActivity extends Activity { // Select the old color mode - nothing really changed. setColorModeSpinnerSelectionNoCallback(oldColorModeNewIndex); } else { - final int selectedColorModeIndex = Integer.numberOfTrailingZeros( - (colorModes & defaultAttributes.getColorMode())); - setColorModeSpinnerSelectionNoCallback(selectedColorModeIndex); - mCurrPrintAttributes.setColorMode(mColorModeSpinnerAdapter - .getItem(selectedColorModeIndex).value); - someAttributeSelectionChanged = true; + final int selectedColorMode = colorModes & defaultAttributes.getColorMode(); + final int itemCount = mColorModeSpinnerAdapter.getCount(); + for (int i = 0; i < itemCount; i++) { + SpinnerItem item = mColorModeSpinnerAdapter.getItem(i); + if (selectedColorMode == item.value) { + setColorModeSpinnerSelectionNoCallback(i); + mCurrPrintAttributes.setColorMode(selectedColorMode); + someAttributeSelectionChanged = true; + } + } } } mColorModeSpinner.setEnabled(true); diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png index 1c3518af96a20edc3e7d5149b6702f81312650e0..f256fbbb246af80301e4e63756fb51a45659aea4 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_available.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png index 9dbc65ec38c3eb5e725fabf3d1c3a8563e2bf008..b946ec9029f757b6a6173b90fabb0cc391261320 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connected.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png index ddb002d39f86bcf004ef005cf9c6bda52c975c67..48606a8d0513d4ea4948e6b1e1c93d772304a52a 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_0.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png index 43b7ef2793d659be0ad7f01c4e75e59b097f1ce1..d006f13e4b4636ac190d2785638176d10747940c 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_1.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png index 1d8b7ee1cc31e66fff6750d15c002cd1ca9b18e7..867947b22d6d6e3148f35fe3c32b0f04499783de 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_cast_connecting_2.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png index 11b2134495aeb42774b74df6488f3fbe63c02463..b1e984c7c0a09b67c9bf59786c707a54cb684a4a 100644 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png and b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_available.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png index a858573e0275adbe101e7dfd03cf27f5b4ce1bbb..ceda1bb6398a1e4beaf800c58bf8d6b9d2f87dad 100644 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png and b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connected.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png index 04de5d7f8d682d914fee51c7548d8e440e2c0ef7..25fc7593beb17dfb9c51217d6b5f7cc93fc92928 100644 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png and b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_0.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png index caea37e6c0c7a6d0d22f4cd7d02497b1111899eb..9dfc3c6537fadc962dbe7a9c21505f6f329b5d8a 100644 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png and b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_1.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png index b66aa46d36d6e0a600669661ec98f880647669a3..82f4113b39b17678dc5ec1c33754f536152e21a2 100644 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png and b/packages/SystemUI/res/drawable-mdpi/ic_qs_cast_connecting_2.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png index 10ebcd5e2ccf0fc5076a7809f794704f5e6d0fec..47be502e0a313bf788e0a6af899f0c17cd9dd89b 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_available.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png index fef43b8ca3e1f23e1464bda18ff8a6d314259827..4b12809b9c967aebf00761282324a0e617fcb6f9 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connected.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png index 05e32677c583f78870f377bf6457a803d4ad2b7b..945c6060055a8d8f4a481e68c324756e2b5a6b44 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_0.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png index ef42b27e94b5ece891e4c4915b513bc9671c98a4..0a3f73e1b7bb8de48c5831b5d6c01e4856da4219 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_1.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png index fc1c95e4dee1a84bd68ab8d428bf8a210eca6148..398cbef8baa0984866f86816a40c757c7f069f97 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_cast_connecting_2.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png index 68b1b7c75b74ea017317931d58dbf1b53af7f6a1..8e225af212efb52ad3a89f471a002e9de416716a 100644 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_available.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png index 8a8f890298b1f8d7b5a69f2cdb96cd588f78e397..937202bdf809db5162c45aefd140d562c3ed7fb0 100644 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connected.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png index 12d4a0178d37bec6095e49a119ecf84d663c1a95..4621d185380a5d44f6fdbac515354b98b12c3e22 100644 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_0.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png index 3cb44217cb12c4eae3ad90a3b0d4b7968afc08bf..a1ab61be9c7dd045435acba9a11f8db9725ed4f4 100644 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_1.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png index 4620b3a2decdfa2bb4d96967ef475e32b6bc4a7e..ea42a7f670b56df42a16afeffc7817f6b8997194 100644 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_cast_connecting_2.png differ diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 4b0c2cb31ee86175b184a36de38d5559a83a97a4..8092ab3e4243b637f6af935eecd734c8186690a3 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -27,13 +27,16 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region.Op; import android.opengl.GLUtils; import android.os.SystemProperties; import android.renderscript.Matrix4f; import android.service.wallpaper.WallpaperService; import android.util.Log; +import android.view.Display; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.WindowManager; @@ -107,10 +110,12 @@ public class ImageWallpaper extends WallpaperService { private WallpaperObserver mReceiver; Bitmap mBackground; + int mBackgroundWidth = -1, mBackgroundHeight = -1; int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; int mLastRotation = -1; - float mXOffset; - float mYOffset; + float mXOffset = 0.5f; + float mYOffset = 0.5f; + float mScale = 1f; boolean mVisible = true; boolean mRedrawNeeded; @@ -155,6 +160,8 @@ public class ImageWallpaper extends WallpaperService { mLastSurfaceWidth = mLastSurfaceHeight = -1; mBackground = null; + mBackgroundWidth = -1; + mBackgroundHeight = -1; mRedrawNeeded = true; drawFrame(); } @@ -173,6 +180,8 @@ public class ImageWallpaper extends WallpaperService { } mBackground.recycle(); mBackground = null; + mBackgroundWidth = -1; + mBackgroundHeight = -1; mWallpaperManager.forgetLoadedWallpaper(); } } @@ -204,21 +213,40 @@ public class ImageWallpaper extends WallpaperService { } } - @Override - public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { - super.onDesiredSizeChanged(desiredWidth, desiredHeight); - SurfaceHolder surfaceHolder = getSurfaceHolder(); - if (surfaceHolder != null) { - updateSurfaceSize(surfaceHolder); + void updateSurfaceSize(SurfaceHolder surfaceHolder) { + Point p = getDefaultDisplaySize(); + + // Load background image dimensions, if we haven't saved them yet + if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) { + // Need to load the image to get dimensions + mWallpaperManager.forgetLoadedWallpaper(); + updateWallpaperLocked(); + if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) { + // Default to the display size if we can't find the dimensions + mBackgroundWidth = p.x; + mBackgroundHeight = p.y; + } + } + + // Force the wallpaper to cover the screen in both dimensions + int surfaceWidth = Math.max(p.x, mBackgroundWidth); + int surfaceHeight = Math.max(p.y, mBackgroundHeight); + + // If the surface dimensions haven't changed, then just return + final Rect frame = surfaceHolder.getSurfaceFrame(); + if (frame != null) { + final int dw = frame.width(); + final int dh = frame.height(); + if (surfaceWidth == dw && surfaceHeight == dh) { + return; + } } - } - void updateSurfaceSize(SurfaceHolder surfaceHolder) { if (FIXED_SIZED_SURFACE) { // Used a fixed size surface, because we are special. We can do // this because we know the current design of window animations doesn't // cause this to break. - surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight()); + surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight); } else { surfaceHolder.setSizeFromLayout(); } @@ -298,13 +326,30 @@ public class ImageWallpaper extends WallpaperService { drawFrame(); } + private Point getDefaultDisplaySize() { + Point p = new Point(); + Context c = ImageWallpaper.this.getApplicationContext(); + WindowManager wm = (WindowManager)c.getSystemService(Context.WINDOW_SERVICE); + Display d = wm.getDefaultDisplay(); + d.getRealSize(p); + return p; + } + void drawFrame() { + int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)). + getDefaultDisplay().getRotation(); + + // Sometimes a wallpaper is not large enough to cover the screen in one dimension. + // Call updateSurfaceSize -- it will only actually do the update if the dimensions + // should change + if (newRotation != mLastRotation) { + // Update surface size (if necessary) + updateSurfaceSize(getSurfaceHolder()); + } SurfaceHolder sh = getSurfaceHolder(); final Rect frame = sh.getSurfaceFrame(); final int dw = frame.width(); final int dh = frame.height(); - int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)). - getDefaultDisplay().getRotation(); boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight; boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation; @@ -343,10 +388,21 @@ public class ImageWallpaper extends WallpaperService { } } - final int availw = dw - mBackground.getWidth(); - final int availh = dh - mBackground.getHeight(); - int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2); - int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2); + // Center the scaled image + mScale = Math.max(1f, Math.max(dw / (float) mBackground.getWidth(), + dh / (float) mBackground.getHeight())); + final int availw = dw - (int) (mBackground.getWidth() * mScale); + final int availh = dh - (int) (mBackground.getHeight() * mScale); + int xPixels = availw / 2; + int yPixels = availh / 2; + + // Adjust the image for xOffset/yOffset values. If window manager is handling offsets, + // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels + // will remain unchanged + final int availwUnscaled = dw - mBackground.getWidth(); + final int availhUnscaled = dh - mBackground.getHeight(); + if (availwUnscaled < 0) xPixels += (int)(availwUnscaled * (mXOffset - .5f) + .5f); + if (availhUnscaled < 0) yPixels += (int)(availhUnscaled * (mYOffset - .5f) + .5f); mOffsetsChanged = false; mRedrawNeeded = false; @@ -354,8 +410,6 @@ public class ImageWallpaper extends WallpaperService { mLastSurfaceWidth = dw; mLastSurfaceHeight = dh; } - mLastXTranslation = xPixels; - mLastYTranslation = yPixels; if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) { if (DEBUG) { Log.d(TAG, "Suppressed drawFrame since the image has not " @@ -363,6 +417,8 @@ public class ImageWallpaper extends WallpaperService { } return; } + mLastXTranslation = xPixels; + mLastYTranslation = yPixels; if (DEBUG) { Log.d(TAG, "Redrawing wallpaper"); @@ -391,7 +447,11 @@ public class ImageWallpaper extends WallpaperService { Throwable exception = null; try { mBackground = null; + mBackgroundWidth = -1; + mBackgroundHeight = -1; mBackground = mWallpaperManager.getBitmap(); + mBackgroundWidth = mBackground.getWidth(); + mBackgroundHeight = mBackground.getHeight(); } catch (RuntimeException e) { exception = e; } catch (OutOfMemoryError e) { @@ -400,6 +460,8 @@ public class ImageWallpaper extends WallpaperService { if (exception != null) { mBackground = null; + mBackgroundWidth = -1; + mBackgroundHeight = -1; // Note that if we do fail at this, and the default wallpaper can't // be loaded, we will go into a cycle. Don't do a build where the // default wallpaper can't be loaded. @@ -413,24 +475,27 @@ public class ImageWallpaper extends WallpaperService { } } - private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) { + private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) { Canvas c = sh.lockCanvas(); if (c != null) { try { if (DEBUG) { - Log.d(TAG, "Redrawing: x=" + x + ", y=" + y); + Log.d(TAG, "Redrawing: left=" + left + ", top=" + top); } - c.translate(x, y); + final float right = left + mBackground.getWidth() * mScale; + final float bottom = top + mBackground.getHeight() * mScale; if (w < 0 || h < 0) { c.save(Canvas.CLIP_SAVE_FLAG); - c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(), + c.clipRect(left, top, right, bottom, Op.DIFFERENCE); c.drawColor(0xff000000); c.restore(); } if (mBackground != null) { - c.drawBitmap(mBackground, 0, 0, null); + RectF dest = new RectF(left, top, right, bottom); + // add a filter bitmap? + c.drawBitmap(mBackground, null, dest, null); } } finally { sh.unlockCanvasAndPost(c); @@ -441,8 +506,8 @@ public class ImageWallpaper extends WallpaperService { private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) { if (!initGL(sh)) return false; - final float right = left + mBackground.getWidth(); - final float bottom = top + mBackground.getHeight(); + final float right = left + mBackground.getWidth() * mScale; + final float bottom = top + mBackground.getHeight() * mScale; final Rect frame = sh.getSurfaceFrame(); final Matrix4f ortho = new Matrix4f(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index cb17ac62439ac08c30318b334120e64f85c0ff0e..eb63a548ac428ae608e4d7e1546edd2b7e0d78b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -37,6 +37,8 @@ public class BarTransitions { private static final boolean DEBUG = false; private static final boolean DEBUG_COLORS = false; + public static final boolean HIGH_END = ActivityManager.isHighEndGfx(); + public static final int MODE_OPAQUE = 0; public static final int MODE_SEMI_TRANSPARENT = 1; public static final int MODE_TRANSLUCENT = 2; @@ -48,7 +50,6 @@ public class BarTransitions { private final String mTag; private final View mView; - private final boolean mSupportsTransitions = ActivityManager.isHighEndGfx(); private final BarBackgroundDrawable mBarBackground; private int mMode; @@ -57,7 +58,7 @@ public class BarTransitions { mTag = "BarTransitions." + view.getClass().getSimpleName(); mView = view; mBarBackground = new BarBackgroundDrawable(mView.getContext(), gradientResourceId); - if (mSupportsTransitions) { + if (HIGH_END) { mView.setBackground(mBarBackground); } } @@ -67,18 +68,22 @@ public class BarTransitions { } public void transitionTo(int mode, boolean animate) { + // low-end devices do not support translucent modes, fallback to opaque + if (!HIGH_END && (mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT)) { + mode = MODE_OPAQUE; + } if (mMode == mode) return; int oldMode = mMode; mMode = mode; if (DEBUG) Log.d(mTag, String.format("%s -> %s animate=%s", modeToString(oldMode), modeToString(mode), animate)); - if (mSupportsTransitions) { - onTransition(oldMode, mMode, animate); - } + onTransition(oldMode, mMode, animate); } protected void onTransition(int oldMode, int newMode, boolean animate) { - applyModeBackground(oldMode, newMode, animate); + if (HIGH_END) { + applyModeBackground(oldMode, newMode, animate); + } } protected void applyModeBackground(int oldMode, int newMode, boolean animate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java index e1a20ecedfd4f16338553e8a3eb778e1b03283bd..42201c5a2d98a68e7beb5488ac5d11cdc0d0f7b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java @@ -692,14 +692,8 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, } else { connectedRoute = null; connecting = false; - final int count = mMediaRouter.getRouteCount(); - for (int i = 0; i < count; i++) { - MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i); - if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) { - enabled = true; - break; - } - } + enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, + MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); } mRemoteDisplayState.enabled = enabled; diff --git a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml index 1622742a52a23e2a792fc4a31d1db934d75ee9a9..2a0188ae2b6adf3d5d313e974a79d16886add12b 100644 --- a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml +++ b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml @@ -20,6 +20,7 @@ false + + true diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/CropView.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/CropView.java index 14f7c1d1f7cf5c08f37e1c05f968a92173c038d9..c3ef3024f4840658b613d0e46f4e852ee4707922 100644 --- a/packages/WallpaperCropper/src/com/android/wallpapercropper/CropView.java +++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/CropView.java @@ -165,7 +165,8 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { final float imageWidth = imageDims[0]; final float imageHeight = imageDims[1]; mMinScale = Math.max(w / imageWidth, h / imageHeight); - mRenderer.scale = Math.max(mMinScale, mRenderer.scale); + mRenderer.scale = + Math.max(mMinScale, resetScale ? Float.MIN_VALUE : mRenderer.scale); } } } diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java index 57c0581b8f87df55acb1e50b8be1ce81739e67c6..757a7a2f0e5c085a01954fd671417f13a8254faa 100644 --- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java +++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java @@ -73,6 +73,7 @@ public class WallpaperCropActivity extends Activity { protected CropView mCropView; protected Uri mUri; + private View mSetWallpaperButton; @Override protected void onCreate(Bundle savedInstanceState) { @@ -109,10 +110,12 @@ public class WallpaperCropActivity extends Activity { cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); } }); + mSetWallpaperButton = findViewById(R.id.set_wallpaper_button); // Load image in background final BitmapRegionTileSource.UriBitmapSource bitmapSource = new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024); + mSetWallpaperButton.setVisibility(View.INVISIBLE); Runnable onLoad = new Runnable() { public void run() { if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { @@ -120,6 +123,8 @@ public class WallpaperCropActivity extends Activity { getString(R.string.wallpaper_load_fail), Toast.LENGTH_LONG).show(); finish(); + } else { + mSetWallpaperButton.setVisibility(View.VISIBLE); } } }; @@ -326,10 +331,10 @@ public class WallpaperCropActivity extends Activity { protected void cropImageAndSetWallpaper(Uri uri, OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { + boolean centerCrop = getResources().getBoolean(R.bool.center_crop); // Get the crop boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - Display d = getWindowManager().getDefaultDisplay(); Point displaySize = new Point(); @@ -340,10 +345,18 @@ public class WallpaperCropActivity extends Activity { getWindowManager()); // Get the crop RectF cropRect = mCropView.getCrop(); + + Point inSize = mCropView.getSourceDimensions(); + + // Due to rounding errors in the cropview renderer the edges can be slightly offset + // therefore we ensure that the boundaries are sanely defined + cropRect.left = Math.max(0, cropRect.left); + cropRect.right = Math.min(inSize.x, cropRect.right); + cropRect.top = Math.max(0, cropRect.top); + cropRect.bottom = Math.min(inSize.y, cropRect.bottom); int cropRotation = mCropView.getImageRotation(); float cropScale = mCropView.getWidth() / (float) cropRect.width(); - Point inSize = mCropView.getSourceDimensions(); Matrix rotateMatrix = new Matrix(); rotateMatrix.setRotate(cropRotation); float[] rotatedInSize = new float[] { inSize.x, inSize.y }; @@ -354,15 +367,25 @@ public class WallpaperCropActivity extends Activity { // ADJUST CROP WIDTH // Extend the crop all the way to the right, for parallax // (or all the way to the left, in RTL) - float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; + float extraSpace; + if (centerCrop) { + extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left); + } else { + extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; + } // Cap the amount of extra width float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width(); extraSpace = Math.min(extraSpace, maxExtraSpace); - if (ltr) { - cropRect.right += extraSpace; + if (centerCrop) { + cropRect.left -= extraSpace / 2f; + cropRect.right += extraSpace / 2f; } else { - cropRect.left -= extraSpace; + if (ltr) { + cropRect.right += extraSpace; + } else { + cropRect.left -= extraSpace; + } } // ADJUST CROP HEIGHT @@ -550,6 +573,8 @@ public class WallpaperCropActivity extends Activity { Rect roundedTrueCrop = new Rect(); Matrix rotateMatrix = new Matrix(); Matrix inverseRotateMatrix = new Matrix(); + + Point bounds = getImageBounds(); if (mRotation > 0) { rotateMatrix.setRotate(mRotation); inverseRotateMatrix.setRotate(-mRotation); @@ -557,7 +582,6 @@ public class WallpaperCropActivity extends Activity { mCropBounds.roundOut(roundedTrueCrop); mCropBounds = new RectF(roundedTrueCrop); - Point bounds = getImageBounds(); if (bounds == null) { Log.w(LOGTAG, "cannot get bounds for image"); failure = true; @@ -629,12 +653,38 @@ public class WallpaperCropActivity extends Activity { Utils.closeSilently(is); } if (fullSize != null) { + // Find out the true sample size that was used by the decoder + scaleDownSampleSize = bounds.x / fullSize.getWidth(); mCropBounds.left /= scaleDownSampleSize; mCropBounds.top /= scaleDownSampleSize; mCropBounds.bottom /= scaleDownSampleSize; mCropBounds.right /= scaleDownSampleSize; mCropBounds.roundOut(roundedTrueCrop); + // Adjust values to account for issues related to rounding + if (roundedTrueCrop.width() > fullSize.getWidth()) { + // Adjust the width + roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth(); + } + if (roundedTrueCrop.right > fullSize.getWidth()) { + // Adjust the left value + int adjustment = roundedTrueCrop.left - + Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width()); + roundedTrueCrop.left -= adjustment; + roundedTrueCrop.right -= adjustment; + } + if (roundedTrueCrop.height() > fullSize.getHeight()) { + // Adjust the height + roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight(); + } + if (roundedTrueCrop.bottom > fullSize.getHeight()) { + // Adjust the top value + int adjustment = roundedTrueCrop.top - + Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height()); + roundedTrueCrop.top -= adjustment; + roundedTrueCrop.bottom -= adjustment; + } + crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, roundedTrueCrop.top, roundedTrueCrop.width(), roundedTrueCrop.height()); @@ -759,14 +809,15 @@ public class WallpaperCropActivity extends Activity { final WallpaperManager wallpaperManager) { final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager); - new Thread("suggestWallpaperDimension") { - public void run() { + new AsyncTask() { + public Void doInBackground(Void ... args) { // If we have saved a wallpaper width/height, use that instead int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x); int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y); wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); + return null; } - }.start(); + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } protected static RectF getMaxCropRect( diff --git a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java index 3e57a77807c46256bb8010fcc3b440ed62802b64..b734c41c9a11c936ba339eaef392d2c0f44e3271 100644 --- a/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java +++ b/policy/src/com/android/internal/policy/impl/ImmersiveModeConfirmation.java @@ -178,6 +178,7 @@ public class ImmersiveModeConfirmation { | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED , PixelFormat.TRANSLUCENT); + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ImmersiveModeConfirmation"); lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; lp.gravity = Gravity.FILL; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index aeaa18f5544276d4ff3a917b08cea73e3d91d1d3..70e45820a450a749b841725714363e6fd91c6f96 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -105,6 +105,7 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.util.HashSet; import static android.view.WindowManager.LayoutParams.*; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; @@ -381,6 +382,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final Rect mTmpNavigationFrame = new Rect(); WindowState mTopFullscreenOpaqueWindowState; + HashSet mAppsToBeHidden = new HashSet(); boolean mTopIsFullscreen; boolean mForceStatusBar; boolean mForceStatusBarFromKeyguard; @@ -3277,8 +3279,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { + mRestrictedScreenWidth; pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight; - } else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT) { - // Toasts are stable to interim decor changes. + } else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT + || attrs.type == TYPE_VOLUME_OVERLAY) { + // These dialogs are stable to interim decor changes. pf.left = df.left = of.left = cf.left = mStableLeft; pf.top = df.top = of.top = cf.top = mStableTop; pf.right = df.right = of.right = cf.right = mStableRight; @@ -3364,6 +3367,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) { mTopFullscreenOpaqueWindowState = null; + mAppsToBeHidden.clear(); mForceStatusBar = false; mForceStatusBarFromKeyguard = false; mForcingShowNavBar = false; @@ -3382,13 +3386,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManager.LayoutParams attrs) { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw=" + win.isVisibleOrBehindKeyguardLw()); - if (mTopFullscreenOpaqueWindowState == null && (win.getAttrs().privateFlags - &WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_NAV_BAR) != 0 - || (win.isVisibleLw() && attrs.type == TYPE_INPUT_METHOD)) { - if (mForcingShowNavBarLayer < 0) { - mForcingShowNavBar = true; - mForcingShowNavBarLayer = win.getSurfaceLayer(); - } + if (mTopFullscreenOpaqueWindowState == null + && win.isVisibleLw() && attrs.type == TYPE_INPUT_METHOD) { + mForcingShowNavBar = true; + mForcingShowNavBarLayer = win.getSurfaceLayer(); } if (mTopFullscreenOpaqueWindowState == null && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) { @@ -3402,7 +3403,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (attrs.type == TYPE_KEYGUARD) { mShowingLockscreen = true; } - boolean applyWindow = attrs.type >= FIRST_APPLICATION_WINDOW + boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW; if (attrs.type == TYPE_DREAM) { // If the lockscreen was showing when the dream started then wait @@ -3410,30 +3411,42 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!mDreamingLockscreen || (win.isVisibleLw() && win.hasDrawnLw())) { mShowingDream = true; - applyWindow = true; + appWindow = true; } } - if (applyWindow - && attrs.x == 0 && attrs.y == 0 - && attrs.width == WindowManager.LayoutParams.MATCH_PARENT - && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) { - if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); - mTopFullscreenOpaqueWindowState = win; - if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) { - if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mHideLockScreen to true by win " + win); - mHideLockScreen = true; - mForceStatusBarFromKeyguard = false; - } - if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0 - && mDismissKeyguard == DISMISS_KEYGUARD_NONE) { - if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mDismissKeyguard true by win " + win); - mDismissKeyguard = mWinDismissingKeyguard == win ? - DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; - mWinDismissingKeyguard = win; - mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure(); + + final boolean showWhenLocked = (attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0; + if (appWindow) { + if (showWhenLocked) { + mAppsToBeHidden.remove(win.getAppToken()); + } else { + mAppsToBeHidden.add(win.getAppToken()); } - if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { - mAllowLockscreenWhenOn = true; + if (attrs.x == 0 && attrs.y == 0 + && attrs.width == WindowManager.LayoutParams.MATCH_PARENT + && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) { + if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); + mTopFullscreenOpaqueWindowState = win; + if (mAppsToBeHidden.isEmpty()) { + if (showWhenLocked) { + if (DEBUG_LAYOUT) Slog.v(TAG, + "Setting mHideLockScreen to true by win " + win); + mHideLockScreen = true; + mForceStatusBarFromKeyguard = false; + } + if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0 + && mDismissKeyguard == DISMISS_KEYGUARD_NONE) { + if (DEBUG_LAYOUT) Slog.v(TAG, + "Setting mDismissKeyguard true by win " + win); + mDismissKeyguard = mWinDismissingKeyguard == win ? + DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; + mWinDismissingKeyguard = win; + mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure(); + } + } + if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { + mAllowLockscreenWhenOn = true; + } } } } @@ -3519,7 +3532,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mKeyguard != null) { if (localLOGV) Slog.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard=" + mHideLockScreen); - if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardDelegate.isSecure()) { + if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !isKeyguardSecure()) { if (mKeyguard.hideLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index baef607a4ec4b2a7dda9370421043cbe4b8d0f93..6d65a7022393db8ba1093b3504404d0a3a1d0798 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -82,7 +82,7 @@ import android.util.StringBuilderPrinter; import com.android.internal.backup.BackupConstants; import com.android.internal.backup.IBackupTransport; import com.android.internal.backup.IObbBackupService; -import com.android.internal.backup.LocalTransport; +import com.android.server.EventLogTags; import com.android.server.PackageManagerBackupAgent.Metadata; import java.io.BufferedInputStream; @@ -140,11 +140,16 @@ class BackupManagerService extends IBackupManager.Stub { private static final boolean DEBUG = true; private static final boolean MORE_DEBUG = false; + // Historical and current algorithm names + static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1"; + static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit"; + // Name and current contents version of the full-backup manifest file static final String BACKUP_MANIFEST_FILENAME = "_manifest"; static final int BACKUP_MANIFEST_VERSION = 1; static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n"; - static final int BACKUP_FILE_VERSION = 1; + static final int BACKUP_FILE_VERSION = 2; + static final int BACKUP_PW_FILE_VERSION = 2; static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup"; @@ -161,6 +166,9 @@ class BackupManagerService extends IBackupManager.Stub { // the first backup pass. private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; + // Retry interval for clear/init when the transport is unavailable + private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR; + private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN"; private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT"; private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR"; @@ -174,6 +182,8 @@ class BackupManagerService extends IBackupManager.Stub { private static final int MSG_RESTORE_TIMEOUT = 8; private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9; private static final int MSG_RUN_FULL_RESTORE = 10; + private static final int MSG_RETRY_INIT = 11; + private static final int MSG_RETRY_CLEAR = 12; // backup task state machine tick static final int MSG_BACKUP_RESTORE_STEP = 20; @@ -306,6 +316,7 @@ class BackupManagerService extends IBackupManager.Stub { class RestoreParams { public IBackupTransport transport; + public String dirName; public IRestoreObserver observer; public long token; public PackageInfo pkgInfo; @@ -313,9 +324,10 @@ class BackupManagerService extends IBackupManager.Stub { public boolean needFullBackup; public String[] filterSet; - RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, + RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) { transport = _transport; + dirName = _dirName; observer = _obs; token = _token; pkgInfo = _pkg; @@ -324,9 +336,10 @@ class BackupManagerService extends IBackupManager.Stub { filterSet = null; } - RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, - boolean _needFullBackup) { + RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, + long _token, boolean _needFullBackup) { transport = _transport; + dirName = _dirName; observer = _obs; token = _token; pkgInfo = null; @@ -335,9 +348,10 @@ class BackupManagerService extends IBackupManager.Stub { filterSet = null; } - RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, - String[] _filterSet, boolean _needFullBackup) { + RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, + long _token, String[] _filterSet, boolean _needFullBackup) { transport = _transport; + dirName = _dirName; observer = _obs; token = _token; pkgInfo = null; @@ -357,6 +371,16 @@ class BackupManagerService extends IBackupManager.Stub { } } + class ClearRetryParams { + public String transportName; + public String packageName; + + ClearRetryParams(String transport, String pkg) { + transportName = transport; + packageName = pkg; + } + } + class FullParams { public ParcelFileDescriptor fd; public final AtomicBoolean latch; @@ -431,6 +455,8 @@ class BackupManagerService extends IBackupManager.Stub { private final SecureRandom mRng = new SecureRandom(); private String mPasswordHash; private File mPasswordHashFile; + private int mPasswordVersion; + private File mPasswordVersionFile; private byte[] mPasswordSalt; // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys @@ -516,13 +542,28 @@ class BackupManagerService extends IBackupManager.Stub { // When it completes successfully, that old journal file will be // deleted. If we crash prior to that, the old journal is parsed // at next boot and the journaled requests fulfilled. + boolean staged = true; if (queue.size() > 0) { // Spin up a backup state sequence and set it running - PerformBackupTask pbt = new PerformBackupTask(transport, queue, oldJournal); - Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); - sendMessage(pbtMessage); + try { + String dirName = transport.transportDirName(); + PerformBackupTask pbt = new PerformBackupTask(transport, dirName, + queue, oldJournal); + Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); + sendMessage(pbtMessage); + } catch (RemoteException e) { + // unable to ask the transport its dir name -- transient failure, since + // the above check succeeded. Try again next time. + Slog.e(TAG, "Transport became unavailable attempting backup"); + staged = false; + } } else { Slog.v(TAG, "Backup requested but nothing pending"); + staged = false; + } + + if (!staged) { + // if we didn't actually hand off the wakelock, rewind until next time synchronized (mQueueLock) { mBackupRunning = false; } @@ -572,7 +613,7 @@ class BackupManagerService extends IBackupManager.Stub { RestoreParams params = (RestoreParams)msg.obj; Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); PerformRestoreTask task = new PerformRestoreTask( - params.transport, params.observer, + params.transport, params.dirName, params.observer, params.token, params.pkgInfo, params.pmToken, params.needFullBackup, params.filterSet); Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task); @@ -599,6 +640,14 @@ class BackupManagerService extends IBackupManager.Stub { break; } + case MSG_RETRY_CLEAR: + { + // reenqueues if the transport remains unavailable + ClearRetryParams params = (ClearRetryParams)msg.obj; + clearBackupData(params.transportName, params.packageName); + break; + } + case MSG_RUN_INITIALIZE: { HashSet queue; @@ -613,6 +662,16 @@ class BackupManagerService extends IBackupManager.Stub { break; } + case MSG_RETRY_INIT: + { + synchronized (mQueueLock) { + recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), + mRunInitIntent); + } + break; + } + case MSG_RUN_GET_RESTORE_SETS: { // Like other async operations, this is entered with the wakelock held @@ -758,6 +817,27 @@ class BackupManagerService extends IBackupManager.Stub { } mDataDir = Environment.getDownloadCacheDirectory(); + mPasswordVersion = 1; // unless we hear otherwise + mPasswordVersionFile = new File(mBaseStateDir, "pwversion"); + if (mPasswordVersionFile.exists()) { + FileInputStream fin = null; + DataInputStream in = null; + try { + fin = new FileInputStream(mPasswordVersionFile); + in = new DataInputStream(fin); + mPasswordVersion = in.readInt(); + } catch (IOException e) { + Slog.e(TAG, "Unable to read backup pw version"); + } finally { + try { + if (in != null) in.close(); + if (fin != null) fin.close(); + } catch (IOException e) { + Slog.w(TAG, "Error closing pw version files"); + } + } + } + mPasswordHashFile = new File(mBaseStateDir, "pwhash"); if (mPasswordHashFile.exists()) { FileInputStream fin = null; @@ -1058,13 +1138,13 @@ class BackupManagerService extends IBackupManager.Stub { } } - private SecretKey buildPasswordKey(String pw, byte[] salt, int rounds) { - return buildCharArrayKey(pw.toCharArray(), salt, rounds); + private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { + return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); } - private SecretKey buildCharArrayKey(char[] pwArray, byte[] salt, int rounds) { + private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { try { - SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); return keyFactory.generateSecret(ks); } catch (InvalidKeySpecException e) { @@ -1075,8 +1155,8 @@ class BackupManagerService extends IBackupManager.Stub { return null; } - private String buildPasswordHash(String pw, byte[] salt, int rounds) { - SecretKey key = buildPasswordKey(pw, salt, rounds); + private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { + SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); if (key != null) { return byteArrayToHex(key.getEncoded()); } @@ -1104,13 +1184,13 @@ class BackupManagerService extends IBackupManager.Stub { return result; } - private byte[] makeKeyChecksum(byte[] pwBytes, byte[] salt, int rounds) { + private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { char[] mkAsChar = new char[pwBytes.length]; for (int i = 0; i < pwBytes.length; i++) { mkAsChar[i] = (char) pwBytes[i]; } - Key checksum = buildCharArrayKey(mkAsChar, salt, rounds); + Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); return checksum.getEncoded(); } @@ -1122,7 +1202,7 @@ class BackupManagerService extends IBackupManager.Stub { } // Backup password management - boolean passwordMatchesSaved(String candidatePw, int rounds) { + boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) { // First, on an encrypted device we require matching the device pw final boolean isEncrypted; try { @@ -1165,7 +1245,7 @@ class BackupManagerService extends IBackupManager.Stub { } else { // hash the stated current pw and compare to the stored one if (candidatePw != null && candidatePw.length() > 0) { - String currentPwHash = buildPasswordHash(candidatePw, mPasswordSalt, rounds); + String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds); if (mPasswordHash.equalsIgnoreCase(currentPwHash)) { // candidate hash matches the stored hash -- the password matches return true; @@ -1180,11 +1260,37 @@ class BackupManagerService extends IBackupManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "setBackupPassword"); - // If the supplied pw doesn't hash to the the saved one, fail - if (!passwordMatchesSaved(currentPw, PBKDF2_HASH_ROUNDS)) { + // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes + final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); + + // If the supplied pw doesn't hash to the the saved one, fail. The password + // might be caught in the legacy crypto mismatch; verify that too. + if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) + && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, + currentPw, PBKDF2_HASH_ROUNDS))) { return false; } + // Snap up to current on the pw file version + mPasswordVersion = BACKUP_PW_FILE_VERSION; + FileOutputStream pwFout = null; + DataOutputStream pwOut = null; + try { + pwFout = new FileOutputStream(mPasswordVersionFile); + pwOut = new DataOutputStream(pwFout); + pwOut.writeInt(mPasswordVersion); + } catch (IOException e) { + Slog.e(TAG, "Unable to write backup pw version; password not changed"); + return false; + } finally { + try { + if (pwOut != null) pwOut.close(); + if (pwFout != null) pwFout.close(); + } catch (IOException e) { + Slog.w(TAG, "Unable to close pw version record"); + } + } + // Clearing the password is okay if (newPw == null || newPw.isEmpty()) { if (mPasswordHashFile.exists()) { @@ -1202,7 +1308,7 @@ class BackupManagerService extends IBackupManager.Stub { try { // Okay, build the hash of the new backup password byte[] salt = randomBytes(PBKDF2_SALT_SIZE); - String newPwHash = buildPasswordHash(newPw, salt, PBKDF2_HASH_ROUNDS); + String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS); OutputStream pwf = null, buffer = null; DataOutputStream out = null; @@ -1245,34 +1351,65 @@ class BackupManagerService extends IBackupManager.Stub { } } + private boolean backupPasswordMatches(String currentPw) { + if (hasBackupPassword()) { + final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); + if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) + && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, + currentPw, PBKDF2_HASH_ROUNDS))) { + if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); + return false; + } + } + return true; + } + // Maintain persistent state around whether need to do an initialize operation. // Must be called with the queue lock held. void recordInitPendingLocked(boolean isPending, String transportName) { if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending + " on transport " + transportName); + mBackupHandler.removeMessages(MSG_RETRY_INIT); + try { IBackupTransport transport = getTransport(transportName); - String transportDirName = transport.transportDirName(); - File stateDir = new File(mBaseStateDir, transportDirName); - File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); - - if (isPending) { - // We need an init before we can proceed with sending backup data. - // Record that with an entry in our set of pending inits, as well as - // journaling it via creation of a sentinel file. - mPendingInits.add(transportName); - try { - (new FileOutputStream(initPendingFile)).close(); - } catch (IOException ioe) { - // Something is badly wrong with our permissions; just try to move on + if (transport != null) { + String transportDirName = transport.transportDirName(); + File stateDir = new File(mBaseStateDir, transportDirName); + File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); + + if (isPending) { + // We need an init before we can proceed with sending backup data. + // Record that with an entry in our set of pending inits, as well as + // journaling it via creation of a sentinel file. + mPendingInits.add(transportName); + try { + (new FileOutputStream(initPendingFile)).close(); + } catch (IOException ioe) { + // Something is badly wrong with our permissions; just try to move on + } + } else { + // No more initialization needed; wipe the journal and reset our state. + initPendingFile.delete(); + mPendingInits.remove(transportName); } - } else { - // No more initialization needed; wipe the journal and reset our state. - initPendingFile.delete(); - mPendingInits.remove(transportName); + return; // done; don't fall through to the error case } } catch (RemoteException e) { - // can't happen; the transport is local + // transport threw when asked its name; fall through to the lookup-failed case + } + + // The named transport doesn't exist or threw. This operation is + // important, so we record the need for a an init and post a message + // to retry the init later. + if (isPending) { + mPendingInits.add(transportName); + mBackupHandler.sendMessageDelayed( + mBackupHandler.obtainMessage(MSG_RETRY_INIT, + (isPending ? 1 : 0), + 0, + transportName), + TRANSPORT_RETRY_INTERVAL); } } @@ -1348,7 +1485,10 @@ class BackupManagerService extends IBackupManager.Stub { } } } catch (RemoteException e) { - // can't happen, the transport is local + // the transport threw when asked its file naming prefs; declare it invalid + Slog.e(TAG, "Unable to register transport as " + name); + mTransportNames.remove(component); + mTransports.remove(name); } } @@ -1668,7 +1808,7 @@ class BackupManagerService extends IBackupManager.Stub { agent = mConnectedAgent; } } catch (RemoteException e) { - // can't happen + // can't happen - ActivityManager is local } } return agent; @@ -1844,17 +1984,13 @@ class BackupManagerService extends IBackupManager.Stub { int mStatus; boolean mFinished; - public PerformBackupTask(IBackupTransport transport, ArrayList queue, - File journal) { + public PerformBackupTask(IBackupTransport transport, String dirName, + ArrayList queue, File journal) { mTransport = transport; mOriginalQueue = queue; mJournal = journal; - try { - mStateDir = new File(mBaseStateDir, transport.transportDirName()); - } catch (RemoteException e) { - // can't happen; the transport is local - } + mStateDir = new File(mBaseStateDir, dirName); mCurrentState = BackupState.INITIAL; mFinished = false; @@ -2100,8 +2236,12 @@ class BackupManagerService extends IBackupManager.Stub { addBackupTrace("success; recording token"); try { mCurrentToken = mTransport.getCurrentRestoreSet(); - } catch (RemoteException e) {} // can't happen - writeRestoreTokens(); + writeRestoreTokens(); + } catch (RemoteException e) { + // nothing for it at this point, unfortunately, but this will be + // recorded the next time we fully succeed. + addBackupTrace("transport threw returning token"); + } } // Set up the next backup pass - at this point we can set mBackupRunning @@ -2322,7 +2462,7 @@ class BackupManagerService extends IBackupManager.Stub { addBackupTrace("unbinding " + mCurrentPackage.packageName); try { // unbind even on timeout, just in case mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); - } catch (RemoteException e) {} + } catch (RemoteException e) { /* can't happen; activity manager is local */ } } } @@ -2644,11 +2784,9 @@ class BackupManagerService extends IBackupManager.Stub { // Verify that the given password matches the currently-active // backup password, if any - if (hasBackupPassword()) { - if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) { - if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); - return; - } + if (!backupPasswordMatches(mCurrentPassword)) { + if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); + return; } // Write the global file header. All strings are UTF-8 encoded; lines end @@ -2656,7 +2794,7 @@ class BackupManagerService extends IBackupManager.Stub { // final '\n'. // // line 1: "ANDROID BACKUP" - // line 2: backup file format version, currently "1" + // line 2: backup file format version, currently "2" // line 3: compressed? "0" if not compressed, "1" if compressed. // line 4: name of encryption algorithm [currently only "none" or "AES-256"] // @@ -2764,7 +2902,7 @@ class BackupManagerService extends IBackupManager.Stub { OutputStream ofstream) throws Exception { // User key will be used to encrypt the master key. byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE); - SecretKey userKey = buildPasswordKey(mEncryptPassword, newUserSalt, + SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt, PBKDF2_HASH_ROUNDS); // the master key is random for each backup @@ -2811,7 +2949,7 @@ class BackupManagerService extends IBackupManager.Stub { // stated number of PBKDF2 rounds IV = c.getIV(); byte[] mk = masterKeySpec.getEncoded(); - byte[] checksum = makeKeyChecksum(masterKeySpec.getEncoded(), + byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(), checksumSalt, PBKDF2_HASH_ROUNDS); ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length @@ -3154,11 +3292,9 @@ class BackupManagerService extends IBackupManager.Stub { FileInputStream rawInStream = null; DataInputStream rawDataIn = null; try { - if (hasBackupPassword()) { - if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) { - if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); - return; - } + if (!backupPasswordMatches(mCurrentPassword)) { + if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); + return; } mBytes = 0; @@ -3179,8 +3315,12 @@ class BackupManagerService extends IBackupManager.Stub { if (Arrays.equals(magicBytes, streamHeader)) { // okay, header looks good. now parse out the rest of the fields. String s = readHeaderLine(rawInStream); - if (Integer.parseInt(s) == BACKUP_FILE_VERSION) { - // okay, it's a version we recognize + final int archiveVersion = Integer.parseInt(s); + if (archiveVersion <= BACKUP_FILE_VERSION) { + // okay, it's a version we recognize. if it's version 1, we may need + // to try two different PBKDF2 regimes to compare checksums. + final boolean pbkdf2Fallback = (archiveVersion == 1); + s = readHeaderLine(rawInStream); compressed = (Integer.parseInt(s) != 0); s = readHeaderLine(rawInStream); @@ -3188,7 +3328,8 @@ class BackupManagerService extends IBackupManager.Stub { // no more header to parse; we're good to go okay = true; } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { - preCompressStream = decodeAesHeaderAndInitialize(s, rawInStream); + preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, + rawInStream); if (preCompressStream != null) { okay = true; } @@ -3248,7 +3389,71 @@ class BackupManagerService extends IBackupManager.Stub { return buffer.toString(); } - InputStream decodeAesHeaderAndInitialize(String encryptionName, InputStream rawInStream) { + InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, + int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, + boolean doLog) { + InputStream result = null; + + try { + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); + SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, + rounds); + byte[] IV = hexToByteArray(userIvHex); + IvParameterSpec ivSpec = new IvParameterSpec(IV); + c.init(Cipher.DECRYPT_MODE, + new SecretKeySpec(userKey.getEncoded(), "AES"), + ivSpec); + byte[] mkCipher = hexToByteArray(masterKeyBlobHex); + byte[] mkBlob = c.doFinal(mkCipher); + + // first, the master key IV + int offset = 0; + int len = mkBlob[offset++]; + IV = Arrays.copyOfRange(mkBlob, offset, offset + len); + offset += len; + // then the master key itself + len = mkBlob[offset++]; + byte[] mk = Arrays.copyOfRange(mkBlob, + offset, offset + len); + offset += len; + // and finally the master key checksum hash + len = mkBlob[offset++]; + byte[] mkChecksum = Arrays.copyOfRange(mkBlob, + offset, offset + len); + + // now validate the decrypted master key against the checksum + byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); + if (Arrays.equals(calculatedCk, mkChecksum)) { + ivSpec = new IvParameterSpec(IV); + c.init(Cipher.DECRYPT_MODE, + new SecretKeySpec(mk, "AES"), + ivSpec); + // Only if all of the above worked properly will 'result' be assigned + result = new CipherInputStream(rawInStream, c); + } else if (doLog) Slog.w(TAG, "Incorrect password"); + } catch (InvalidAlgorithmParameterException e) { + if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); + } catch (BadPaddingException e) { + // This case frequently occurs when the wrong password is used to decrypt + // the master key. Use the identical "incorrect password" log text as is + // used in the checksum failure log in order to avoid providing additional + // information to an attacker. + if (doLog) Slog.w(TAG, "Incorrect password"); + } catch (IllegalBlockSizeException e) { + if (doLog) Slog.w(TAG, "Invalid block size in master key"); + } catch (NoSuchAlgorithmException e) { + if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); + } catch (NoSuchPaddingException e) { + if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); + } catch (InvalidKeyException e) { + if (doLog) Slog.w(TAG, "Illegal password; aborting"); + } + + return result; + } + + InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, + InputStream rawInStream) { InputStream result = null; try { if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { @@ -3265,59 +3470,13 @@ class BackupManagerService extends IBackupManager.Stub { String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 // decrypt the master key blob - Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); - SecretKey userKey = buildPasswordKey(mDecryptPassword, userSalt, - rounds); - byte[] IV = hexToByteArray(userIvHex); - IvParameterSpec ivSpec = new IvParameterSpec(IV); - c.init(Cipher.DECRYPT_MODE, - new SecretKeySpec(userKey.getEncoded(), "AES"), - ivSpec); - byte[] mkCipher = hexToByteArray(masterKeyBlobHex); - byte[] mkBlob = c.doFinal(mkCipher); - - // first, the master key IV - int offset = 0; - int len = mkBlob[offset++]; - IV = Arrays.copyOfRange(mkBlob, offset, offset + len); - offset += len; - // then the master key itself - len = mkBlob[offset++]; - byte[] mk = Arrays.copyOfRange(mkBlob, - offset, offset + len); - offset += len; - // and finally the master key checksum hash - len = mkBlob[offset++]; - byte[] mkChecksum = Arrays.copyOfRange(mkBlob, - offset, offset + len); - - // now validate the decrypted master key against the checksum - byte[] calculatedCk = makeKeyChecksum(mk, ckSalt, rounds); - if (Arrays.equals(calculatedCk, mkChecksum)) { - ivSpec = new IvParameterSpec(IV); - c.init(Cipher.DECRYPT_MODE, - new SecretKeySpec(mk, "AES"), - ivSpec); - // Only if all of the above worked properly will 'result' be assigned - result = new CipherInputStream(rawInStream, c); - } else Slog.w(TAG, "Incorrect password"); + result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, + rounds, userIvHex, masterKeyBlobHex, rawInStream, false); + if (result == null && pbkdf2Fallback) { + result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, + rounds, userIvHex, masterKeyBlobHex, rawInStream, true); + } } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); - } catch (InvalidAlgorithmParameterException e) { - Slog.e(TAG, "Needed parameter spec unavailable!", e); - } catch (BadPaddingException e) { - // This case frequently occurs when the wrong password is used to decrypt - // the master key. Use the identical "incorrect password" log text as is - // used in the checksum failure log in order to avoid providing additional - // information to an attacker. - Slog.w(TAG, "Incorrect password"); - } catch (IllegalBlockSizeException e) { - Slog.w(TAG, "Invalid block size in master key"); - } catch (NoSuchAlgorithmException e) { - Slog.e(TAG, "Needed decryption algorithm unavailable!"); - } catch (NoSuchPaddingException e) { - Slog.e(TAG, "Needed padding mechanism unavailable!"); - } catch (InvalidKeyException e) { - Slog.w(TAG, "Illegal password; aborting"); } catch (NumberFormatException e) { Slog.w(TAG, "Can't parse restore data header"); } catch (IOException e) { @@ -4337,7 +4496,7 @@ class BackupManagerService extends IBackupManager.Stub { } } - PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer, + PerformRestoreTask(IBackupTransport transport, String dirName, IRestoreObserver observer, long restoreSetToken, PackageInfo targetPackage, int pmToken, boolean needFullBackup, String[] filterSet) { mCurrentState = RestoreState.INITIAL; @@ -4360,11 +4519,7 @@ class BackupManagerService extends IBackupManager.Stub { mFilterSet = null; } - try { - mStateDir = new File(mBaseStateDir, transport.transportDirName()); - } catch (RemoteException e) { - // can't happen; the transport is local - } + mStateDir = new File(mBaseStateDir, dirName); } // Execute one tick of whatever state machine the task implements @@ -5090,8 +5245,8 @@ class BackupManagerService extends IBackupManager.Stub { } // Clear the given package's backup data from the current transport - public void clearBackupData(String packageName) { - if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName); + public void clearBackupData(String transportName, String packageName) { + if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); PackageInfo info; try { info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); @@ -5122,13 +5277,22 @@ class BackupManagerService extends IBackupManager.Stub { // Is the given app an available participant? if (apps.contains(packageName)) { - if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); // found it; fire off the clear request + if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); + mBackupHandler.removeMessages(MSG_RETRY_CLEAR); synchronized (mQueueLock) { + final IBackupTransport transport = getTransport(transportName); + if (transport == null) { + // transport is currently unavailable -- make sure to retry + Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, + new ClearRetryParams(transportName, packageName)); + mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); + return; + } long oldId = Binder.clearCallingIdentity(); mWakelock.acquire(); Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, - new ClearParams(getTransport(mCurrentTransport), info)); + new ClearParams(transport, info)); mBackupHandler.sendMessage(msg); Binder.restoreCallingIdentity(oldId); } @@ -5620,31 +5784,55 @@ class BackupManagerService extends IBackupManager.Stub { return; } + boolean skip = false; + long restoreSet = getAvailableRestoreToken(packageName); if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName + " token=" + Integer.toHexString(token) + " restoreSet=" + Long.toHexString(restoreSet)); + if (restoreSet == 0) { + if (MORE_DEBUG) Slog.i(TAG, "No restore set"); + skip = true; + } - if (mAutoRestore && mProvisioned && restoreSet != 0) { - // okay, we're going to attempt a restore of this package from this restore set. - // The eventual message back into the Package Manager to run the post-install - // steps for 'token' will be issued from the restore handling code. + // Do we have a transport to fetch data for us? + IBackupTransport transport = getTransport(mCurrentTransport); + if (transport == null) { + if (DEBUG) Slog.w(TAG, "No transport"); + skip = true; + } - // We can use a synthetic PackageInfo here because: - // 1. We know it's valid, since the Package Manager supplied the name - // 2. Only the packageName field will be used by the restore code - PackageInfo pkg = new PackageInfo(); - pkg.packageName = packageName; + if (!skip && mAutoRestore && mProvisioned) { + try { + // okay, we're going to attempt a restore of this package from this restore set. + // The eventual message back into the Package Manager to run the post-install + // steps for 'token' will be issued from the restore handling code. - mWakelock.acquire(); - Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); - msg.obj = new RestoreParams(getTransport(mCurrentTransport), null, - restoreSet, pkg, token, true); - mBackupHandler.sendMessage(msg); - } else { + // This can throw and so *must* happen before the wakelock is acquired + String dirName = transport.transportDirName(); + + // We can use a synthetic PackageInfo here because: + // 1. We know it's valid, since the Package Manager supplied the name + // 2. Only the packageName field will be used by the restore code + PackageInfo pkg = new PackageInfo(); + pkg.packageName = packageName; + + mWakelock.acquire(); + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(transport, dirName, null, + restoreSet, pkg, token, true); + mBackupHandler.sendMessage(msg); + } catch (RemoteException e) { + // Binding to the transport broke; back off and proceed with the installation. + Slog.e(TAG, "Unable to contact transport"); + skip = true; + } + } + + if (skip) { // Auto-restore disabled or no way to attempt a restore; just tell the Package // Manager to proceed with the post-install handling for this package. - if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore"); + if (DEBUG) Slog.v(TAG, "Skipping"); try { mPackageManagerBinder.finishPackageInstall(token); } catch (RemoteException e) { /* can't happen */ } @@ -5797,13 +5985,23 @@ class BackupManagerService extends IBackupManager.Stub { return -1; } + String dirName; + try { + dirName = mRestoreTransport.transportDirName(); + } catch (RemoteException e) { + // Transport went AWOL; fail. + Slog.e(TAG, "Unable to contact transport for restore"); + return -1; + } + synchronized (mQueueLock) { for (int i = 0; i < mRestoreSets.length; i++) { if (token == mRestoreSets[i].token) { long oldId = Binder.clearCallingIdentity(); mWakelock.acquire(); Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); - msg.obj = new RestoreParams(mRestoreTransport, observer, token, true); + msg.obj = new RestoreParams(mRestoreTransport, dirName, + observer, token, true); mBackupHandler.sendMessage(msg); Binder.restoreCallingIdentity(oldId); return 0; @@ -5857,13 +6055,22 @@ class BackupManagerService extends IBackupManager.Stub { return -1; } + String dirName; + try { + dirName = mRestoreTransport.transportDirName(); + } catch (RemoteException e) { + // Transport went AWOL; fail. + Slog.e(TAG, "Unable to contact transport for restore"); + return -1; + } + synchronized (mQueueLock) { for (int i = 0; i < mRestoreSets.length; i++) { if (token == mRestoreSets[i].token) { long oldId = Binder.clearCallingIdentity(); mWakelock.acquire(); Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); - msg.obj = new RestoreParams(mRestoreTransport, observer, token, + msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, packages, true); mBackupHandler.sendMessage(msg); Binder.restoreCallingIdentity(oldId); @@ -5929,11 +6136,21 @@ class BackupManagerService extends IBackupManager.Stub { return -1; } + String dirName; + try { + dirName = mRestoreTransport.transportDirName(); + } catch (RemoteException e) { + // Transport went AWOL; fail. + Slog.e(TAG, "Unable to contact transport for restore"); + return -1; + } + // Ready to go: enqueue the restore request and claim success long oldId = Binder.clearCallingIdentity(); mWakelock.acquire(); Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); - msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false); + msg.obj = new RestoreParams(mRestoreTransport, dirName, + observer, token, app, 0, false); mBackupHandler.sendMessage(msg); Binder.restoreCallingIdentity(oldId); return 0; diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index d42ae3a945dbceb32b111049e350388fd12b1f02..b7a1a55e14fccfe3061342135f4ebe00001b3cd4 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -32,6 +32,7 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import android.app.AlarmManager; +import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -43,7 +44,9 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -412,6 +415,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private SettingsObserver mSettingsObserver; + private AppOpsManager mAppOpsManager; + NetworkConfig[] mNetConfigs; int mNetworksDefined; @@ -695,6 +700,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { filter = new IntentFilter(); filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION); mContext.registerReceiver(mProvisioningReceiver, filter); + + mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); } /** @@ -1526,6 +1533,40 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + /** + * Check if the address falls into any of currently running VPN's route's. + */ + private boolean isAddressUnderVpn(InetAddress address) { + synchronized (mVpns) { + synchronized (mRoutesLock) { + int uid = UserHandle.getCallingUserId(); + Vpn vpn = mVpns.get(uid); + if (vpn == null) { + return false; + } + + // Check if an exemption exists for this address. + for (LinkAddress destination : mExemptAddresses) { + if (!NetworkUtils.addressTypeMatches(address, destination.getAddress())) { + continue; + } + + int prefix = destination.getNetworkPrefixLength(); + InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix); + InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(), + prefix); + + if (addrMasked.equals(destMasked)) { + return false; + } + } + + // Finally check if the address is covered by the VPN. + return vpn.isAddressCovered(address); + } + } + } + /** * @deprecated use requestRouteToHostAddress instead * @@ -1537,14 +1578,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { * desired * @return {@code true} on success, {@code false} on failure */ - public boolean requestRouteToHost(int networkType, int hostAddress) { + public boolean requestRouteToHost(int networkType, int hostAddress, String packageName) { InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress); if (inetAddress == null) { return false; } - return requestRouteToHostAddress(networkType, inetAddress.getAddress()); + return requestRouteToHostAddress(networkType, inetAddress.getAddress(), packageName); } /** @@ -1556,11 +1597,40 @@ public class ConnectivityService extends IConnectivityManager.Stub { * desired * @return {@code true} on success, {@code false} on failure */ - public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) { + public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress, + String packageName) { enforceChangePermission(); if (mProtectedNetworks.contains(networkType)) { enforceConnectivityInternalPermission(); } + boolean exempt; + InetAddress addr; + try { + addr = InetAddress.getByAddress(hostAddress); + } catch (UnknownHostException e) { + if (DBG) log("requestRouteToHostAddress got " + e.toString()); + return false; + } + // System apps may request routes bypassing the VPN to keep other networks working. + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + exempt = true; + } else { + mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName); + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName, + 0); + exempt = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } catch (NameNotFoundException e) { + throw new IllegalArgumentException("Failed to find calling package details", e); + } + } + + // Non-exempt routeToHost's can only be added if the host is not covered by the VPN. + // This can be either because the VPN's routes do not cover the destination or a + // system application added an exemption that covers this destination. + if (!exempt && isAddressUnderVpn(addr)) { + return false; + } if (!ConnectivityManager.isNetworkTypeValid(networkType)) { if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType); @@ -1587,18 +1657,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } final long token = Binder.clearCallingIdentity(); try { - InetAddress addr = InetAddress.getByAddress(hostAddress); LinkProperties lp = tracker.getLinkProperties(); - boolean ok = addRouteToAddress(lp, addr, EXEMPT); + boolean ok = addRouteToAddress(lp, addr, exempt); if (DBG) log("requestRouteToHostAddress ok=" + ok); return ok; - } catch (UnknownHostException e) { - if (DBG) log("requestRouteToHostAddress got " + e.toString()); } finally { Binder.restoreCallingIdentity(token); } - if (DBG) log("requestRouteToHostAddress X bottom return false"); - return false; } private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, @@ -2308,9 +2373,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { mInetConditionChangeInFlight = false; // Don't do this - if we never sign in stay, grey //reportNetworkCondition(mActiveDefaultNetwork, 100); + updateNetworkSettings(thisNet); } thisNet.setTeardownRequested(false); - updateNetworkSettings(thisNet); updateMtuSizeSettings(thisNet); handleConnectivityChange(newNetType, false); sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay()); @@ -2695,6 +2760,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { } setBufferSize(bufferSizes); } + + final String defaultRwndKey = "net.tcp.default_init_rwnd"; + int defaultRwndValue = SystemProperties.getInt(defaultRwndKey, 0); + Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TCP_DEFAULT_INIT_RWND, defaultRwndValue); + final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd"; + if (rwndValue != 0) { + SystemProperties.set(sysctlKey, rwndValue.toString()); + } } /** @@ -3037,7 +3111,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: { info = (NetworkInfo) msg.obj; int type = info.getType(); - updateNetworkSettings(mNetTrackers[type]); + if (mNetConfigs[type].isDefault()) updateNetworkSettings(mNetTrackers[type]); break; } } @@ -3596,8 +3670,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int user = UserHandle.getUserId(Binder.getCallingUid()); if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) { synchronized(mVpns) { - mVpns.get(user).protect(socket, - mNetTrackers[type].getLinkProperties().getInterfaceName()); + mVpns.get(user).protect(socket); } return true; } @@ -3837,7 +3910,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { boolean forwardDns) { try { mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd); - if (forwardDns) mNetd.clearDnsInterfaceForUidRange(uidStart, uidEnd); + if (forwardDns) mNetd.clearDnsInterfaceForUidRange(interfaze, uidStart, uidEnd); } catch (RemoteException e) { } @@ -3992,6 +4065,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5; + /** + * The mobile network is provisioning + */ + private static final int CMP_RESULT_CODE_IS_PROVISIONING = 6; + + private AtomicBoolean mIsProvisioningNetwork = new AtomicBoolean(false); + private AtomicBoolean mIsStartingProvisioning = new AtomicBoolean(false); + private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false); @Override @@ -4062,11 +4143,25 @@ public class ConnectivityService extends IConnectivityManager.Stub { setProvNotificationVisible(true, ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(), url); + // Mark that we've got a provisioning network and + // Disable Mobile Data until user actually starts provisioning. + mIsProvisioningNetwork.set(true); + MobileDataStateTracker mdst = (MobileDataStateTracker) + mNetTrackers[ConnectivityManager.TYPE_MOBILE]; + mdst.setInternalDataEnable(false); } else { if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url"); } break; } + case CMP_RESULT_CODE_IS_PROVISIONING: { + // FIXME: Need to know when provisioning is done. Probably we can + // check the completion status if successful we're done if we + // "timedout" or still connected to provisioning APN turn off data? + if (DBG) log("CheckMp.onComplete: provisioning started"); + mIsStartingProvisioning.set(false); + break; + } default: { loge("CheckMp.onComplete: ignore unexpected result=" + result); break; @@ -4216,6 +4311,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { return result; } + if (mCs.mIsStartingProvisioning.get()) { + result = CMP_RESULT_CODE_IS_PROVISIONING; + log("isMobileOk: X is provisioning result=" + result); + return result; + } + // See if we've already determined we've got a provisioning connection, // if so we don't need to do anything active. MobileDataStateTracker mdstDefault = (MobileDataStateTracker) @@ -4354,7 +4455,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // Make a route to host so we check the specific interface. if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI, - hostAddr.getAddress())) { + hostAddr.getAddress(), null)) { // Wait a short time to be sure the route is established ?? log("isMobileOk:" + " wait to establish route to hostAddr=" + hostAddr); @@ -4550,19 +4651,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { }; private void handleMobileProvisioningAction(String url) { - // Notication mark notification as not visible + // Mark notification as not visible setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null); // If provisioning network handle as a special case, // otherwise launch browser with the intent directly. - NetworkInfo ni = getProvisioningNetworkInfo(); - if ((ni != null) && ni.isConnectedToProvisioningNetwork()) { - if (DBG) log("handleMobileProvisioningAction: on provisioning network"); + if (mIsProvisioningNetwork.get()) { + if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch"); + mIsStartingProvisioning.set(true); MobileDataStateTracker mdst = (MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE]; + mdst.setEnableFailFastMobileData(DctConstants.ENABLED); mdst.enableMobileProvisioning(url); } else { - if (DBG) log("handleMobileProvisioningAction: on default network"); + if (DBG) log("handleMobileProvisioningAction: not prov network, launch browser directly"); Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER); newIntent.setData(Uri.parse(url)); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index b4a982e2ab89b441354d827e8e2a99e6ac439178..e0b568b0f638522a657bc3e935bbe10c8a8224a5 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -1151,6 +1151,11 @@ public class LocationManagerService extends ILocationManager.Stub { boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name); if (isEnabled && !shouldBeEnabled) { updateProviderListenersLocked(name, false, mCurrentUserId); + // If any provider has been disabled, clear all last locations for all providers. + // This is to be on the safe side in case a provider has location derived from + // this disabled provider. + mLastLocation.clear(); + mLastLocationCoarseInterval.clear(); changesMade = true; } else if (!isEnabled && shouldBeEnabled) { updateProviderListenersLocked(name, true, mCurrentUserId); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index e6c5422a8fc119325086a4574e31342256060ba8..e83d3769c6f10cc80fc4cb291563129201fd7ab0 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -1613,10 +1613,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void clearDnsInterfaceForUidRange(int uid_start, int uid_end) { + public void clearDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("resolver", "clearifaceforuidrange", uid_start, uid_end); + mConnector.execute("resolver", "clearifaceforuidrange", iface, uid_start, uid_end); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 04386759df509e451898cb1e00813ca1e6c58eeb..dedc9bd48c7e6e51cf0a4d4a979a667a74f879f8 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -260,10 +260,11 @@ public class NotificationManagerService extends INotificationManager.Stub @Override public void binderDied() { - if (connection == null) { - // This is not a service; it won't be recreated. We can give up this connection. - unregisterListener(this.listener, this.userid); - } + // Remove the listener, but don't unbind from the service. The system will bring the + // service back up, and the onServiceConnected handler will readd the listener with the + // new binding. If this isn't a bound service, and is just a registered + // INotificationListener, just removing it from the list is all we need to do anyway. + removeListenerImpl(this.listener, this.userid); } /** convenience method for looking in mEnabledListenersForCurrentUser */ @@ -757,26 +758,36 @@ public class NotificationManagerService extends INotificationManager.Stub } /** - * Remove a listener binder directly + * Removes a listener from the list and unbinds from its service. */ - @Override - public void unregisterListener(INotificationListener listener, int userid) { - // no need to check permissions; if your listener binder is in the list, - // that's proof that you had permission to add it in the first place + public void unregisterListener(final INotificationListener listener, final int userid) { + if (listener == null) return; + + NotificationListenerInfo info = removeListenerImpl(listener, userid); + if (info != null && info.connection != null) { + mContext.unbindService(info.connection); + } + } + /** + * Removes a listener from the list but does not unbind from the listener's service. + * + * @return the removed listener. + */ + NotificationListenerInfo removeListenerImpl( + final INotificationListener listener, final int userid) { + NotificationListenerInfo listenerInfo = null; synchronized (mNotificationList) { final int N = mListeners.size(); for (int i=N-1; i>=0; i--) { final NotificationListenerInfo info = mListeners.get(i); if (info.listener.asBinder() == listener.asBinder() && info.userid == userid) { - mListeners.remove(i); - if (info.connection != null) { - mContext.unbindService(info.connection); - } + listenerInfo = mListeners.remove(i); } } } + return listenerInfo; } /** diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index e06b8c53a871a1534228054d9fb420656784d164..d66c5a715d335f822eb2468633ef22adabe56933 100755 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -64,7 +64,7 @@ public final class ActiveServices { static final boolean DEBUG_SERVICE = ActivityManagerService.DEBUG_SERVICE; static final boolean DEBUG_SERVICE_EXECUTING = ActivityManagerService.DEBUG_SERVICE_EXECUTING; static final boolean DEBUG_DELAYED_SERVICE = ActivityManagerService.DEBUG_SERVICE; - static final boolean DEBUG_DELAYED_STATS = DEBUG_DELAYED_SERVICE; + static final boolean DEBUG_DELAYED_STARTS = DEBUG_DELAYED_SERVICE; static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU; static final String TAG = ActivityManagerService.TAG; static final String TAG_MU = ActivityManagerService.TAG_MU; @@ -186,11 +186,11 @@ public final class ActiveServices { void ensureNotStartingBackground(ServiceRecord r) { if (mStartingBackground.remove(r)) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "No longer background starting: " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "No longer background starting: " + r); rescheduleDelayedStarts(); } if (mDelayedStartList.remove(r)) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "No longer delaying start: " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "No longer delaying start: " + r); } } @@ -209,7 +209,7 @@ public final class ActiveServices { while (mDelayedStartList.size() > 0 && mStartingBackground.size() < mMaxStartingBackground) { ServiceRecord r = mDelayedStartList.remove(0); - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "REM FR DELAY LIST (exec next): " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "REM FR DELAY LIST (exec next): " + r); if (r.pendingStarts.size() <= 0) { Slog.w(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested + " delayedStop=" + r.delayedStop); @@ -277,7 +277,7 @@ public final class ActiveServices { ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, int userId) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "startService: " + service + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); final boolean callerFg; @@ -337,7 +337,7 @@ public final class ActiveServices { if (r.delayed) { // This service is already scheduled for a delayed start; just leave // it still waiting. - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Continuing to delay: " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Continuing to delay: " + r); return r.name; } if (smap.mStartingBackground.size() >= mMaxStartingBackground) { @@ -347,15 +347,15 @@ public final class ActiveServices { r.delayed = true; return r.name; } - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Not delaying: " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Not delaying: " + r); addToStarting = true; } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) { // We slightly loosen when we will enqueue this new service as a background // starting service we are waiting for, to also include processes that are // currently running other services or receivers. addToStarting = true; - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Not delaying, but counting as bg: " + r); - } else if (DEBUG_DELAYED_STATS) { + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Not delaying, but counting as bg: " + r); + } else if (DEBUG_DELAYED_STARTS) { StringBuilder sb = new StringBuilder(128); sb.append("Not potential delay (state=").append(proc.curProcState) .append(' ').append(proc.adjType); @@ -368,7 +368,7 @@ public final class ActiveServices { sb.append(r.toString()); Slog.v(TAG, sb.toString()); } - } else if (DEBUG_DELAYED_STATS) { + } else if (DEBUG_DELAYED_STARTS) { if (callerFg) { Slog.v(TAG, "Not potential delay (callerFg=" + callerFg + " uid=" + callingUid + " pid=" + callingPid + "): " + r); @@ -405,7 +405,7 @@ public final class ActiveServices { RuntimeException here = new RuntimeException("here"); here.fillInStackTrace(); Slog.v(TAG, "Starting background (first=" + first + "): " + r, here); - } else if (DEBUG_DELAYED_STATS) { + } else if (DEBUG_DELAYED_STARTS) { Slog.v(TAG, "Starting background (first=" + first + "): " + r); } if (first) { @@ -423,7 +423,7 @@ public final class ActiveServices { // If service isn't actually running, but is is being held in the // delayed list, then we need to keep it started but note that it // should be stopped once no longer delayed. - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Delaying stop of pending: " + service); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Delaying stop of pending: " + service); service.delayedStop = true; return; } @@ -1279,7 +1279,7 @@ public final class ActiveServices { // Make sure this service is no longer considered delayed, we are starting it now. if (r.delayed) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "REM FR DELAY LIST (bring up): " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "REM FR DELAY LIST (bring up): " + r); getServiceMap(r.userId).mDelayedStartList.remove(r); r.delayed = false; } @@ -1362,7 +1362,7 @@ public final class ActiveServices { // Oh and hey we've already been asked to stop! r.delayedStop = false; if (r.startRequested) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Applying delayed stop (in bring up): " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Applying delayed stop (in bring up): " + r); stopServiceLocked(r); } } @@ -1433,7 +1433,7 @@ public final class ActiveServices { sendServiceArgsLocked(r, execInFg, true); if (r.delayed) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "REM FR DELAY LIST (new proc): " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "REM FR DELAY LIST (new proc): " + r); getServiceMap(r.userId).mDelayedStartList.remove(r); r.delayed = false; } @@ -1442,7 +1442,7 @@ public final class ActiveServices { // Oh and hey we've already been asked to stop! r.delayedStop = false; if (r.startRequested) { - if (DEBUG_DELAYED_STATS) Slog.v(TAG, "Applying delayed stop (from start): " + r); + if (DEBUG_DELAYED_STARTS) Slog.v(TAG, "Applying delayed stop (from start): " + r); stopServiceLocked(r); } } @@ -1848,7 +1848,7 @@ public final class ActiveServices { } } if (finishing) { - if (r.app != null) { + if (r.app != null && !r.app.persistent) { r.app.services.remove(r); } r.app = null; @@ -1931,7 +1931,9 @@ public final class ActiveServices { Slog.i(TAG, " Force stopping service " + service); if (service.app != null) { service.app.removed = true; - service.app.services.remove(service); + if (!service.app.persistent) { + service.app.services.remove(service); + } } service.app = null; service.isolatedProc = null; @@ -2033,7 +2035,7 @@ public final class ActiveServices { synchronized (sr.stats.getBatteryStats()) { sr.stats.stopLaunchedLocked(); } - if (sr.app != null) { + if (sr.app != app && sr.app != null && !sr.app.persistent) { sr.app.services.remove(sr); } sr.app = null; @@ -2066,13 +2068,19 @@ public final class ActiveServices { // Now do remaining service cleanup. for (int i=app.services.size()-1; i>=0; i--) { ServiceRecord sr = app.services.valueAt(i); + + // Unless the process is persistent, this process record is going away, + // so make sure the service is cleaned out of it. + if (!app.persistent) { + app.services.removeAt(i); + } + // Sanity check: if the service listed for the app is not one - // we actually are maintaining, drop it. + // we actually are maintaining, just let it drop. if (smap.mServicesByName.get(sr.name) != sr) { ServiceRecord cur = smap.mServicesByName.get(sr.name); Slog.wtf(TAG, "Service " + sr + " in process " + app + " not same as in map: " + cur); - app.services.removeAt(i); continue; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 16b9963e6cf2d74fa8066b0f5617a788206c40ac..3849ab166156bc38ebf64dfde1b4e6fa0f160b67 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1328,7 +1328,7 @@ public final class ActivityManagerService extends ActivityManagerNative String pkg = bundle.getString("pkg"); String reason = bundle.getString("reason"); forceStopPackageLocked(pkg, appid, restart, false, true, false, - UserHandle.USER_ALL, reason); + false, UserHandle.USER_ALL, reason); } } break; case FINALIZE_PENDING_INTENT_MSG: { @@ -4515,7 +4515,7 @@ public final class ActivityManagerService extends ActivityManagerNative private void forceStopPackageLocked(final String packageName, int uid, String reason) { forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false, - false, true, false, UserHandle.getUserId(uid), reason); + false, true, false, false, UserHandle.getUserId(uid), reason); Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, Uri.fromParts("package", packageName, null)); if (!mProcessesReady) { @@ -4531,7 +4531,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private void forceStopUserLocked(int userId, String reason) { - forceStopPackageLocked(null, -1, false, false, true, false, userId, reason); + forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason); Intent intent = new Intent(Intent.ACTION_USER_STOPPED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); @@ -4616,7 +4616,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean forceStopPackageLocked(String name, int appId, boolean callerWillRestart, boolean purgeCache, boolean doit, - boolean evenPersistent, int userId, String reason) { + boolean evenPersistent, boolean uninstalling, int userId, String reason) { int i; int N; @@ -4708,7 +4708,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Remove transient permissions granted from/to this package/user removeUriPermissionsForPackageLocked(name, userId, false); - if (name == null) { + if (name == null || uninstalling) { // Remove pending intents. For now we only do this when force // stopping users, because we have some problems when doing this // for packages -- app widgets are not currently cleaned up for @@ -5153,7 +5153,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (pkgs != null) { for (String pkg : pkgs) { synchronized (ActivityManagerService.this) { - if (forceStopPackageLocked(pkg, -1, false, false, false, false, 0, + if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, 0, "finished booting")) { setResultCode(Activity.RESULT_OK); return; @@ -8466,7 +8466,7 @@ public final class ActivityManagerService extends ActivityManagerNative mDebugTransient = !persistent; if (packageName != null) { forceStopPackageLocked(packageName, -1, false, false, true, true, - UserHandle.USER_ALL, "set debug app"); + false, UserHandle.USER_ALL, "set debug app"); } } } finally { @@ -9246,8 +9246,13 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityInfo ai = ris.get(i).activityInfo; ComponentName comp = new ComponentName(ai.packageName, ai.name); if (lastDoneReceivers.contains(comp)) { + // We already did the pre boot receiver for this app with the current + // platform version, so don't do it again... ris.remove(i); i--; + // ...however, do keep it as one that has been done, so we don't + // forget about it when rewriting the file of last done receivers. + doneReceivers.add(comp); } } @@ -13395,7 +13400,7 @@ public final class ActivityManagerService extends ActivityManagerNative String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); if (list != null && (list.length > 0)) { for (String pkg : list) { - forceStopPackageLocked(pkg, -1, false, true, true, false, userId, + forceStopPackageLocked(pkg, -1, false, true, true, false, false, userId, "storage unmount"); } sendPackageBroadcastLocked( @@ -13407,10 +13412,13 @@ public final class ActivityManagerService extends ActivityManagerNative if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals( intent.getAction()); + boolean fullUninstall = removed && + !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { forceStopPackageLocked(ssp, UserHandle.getAppId( intent.getIntExtra(Intent.EXTRA_UID, -1)), false, true, true, - false, userId, removed ? "pkg removed" : "pkg changed"); + false, fullUninstall, userId, + removed ? "pkg removed" : "pkg changed"); } if (removed) { sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED, @@ -13897,7 +13905,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); // Instrumentation can kill and relaunch even persistent processes - forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId, + forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId, "start instr"); ProcessRecord app = addAppLocked(ai, false); app.instrumentationClass = className; @@ -13965,7 +13973,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.instrumentationProfileFile = null; app.instrumentationArguments = null; - forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId, + forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId, "finished inst"); } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 07c2201bc82df2dcda94584cbaf5f3a161fb060f..4d6727c84f5cf8b44b30e722a08b19ff0166809f 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1289,17 +1289,7 @@ final class ActivityStack { if (prevTask != null && prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); if (prevTask == nextTask) { - ArrayList activities = prevTask.mActivities; - final int numActivities = activities.size(); - for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - // r is usually the same as next, but what if two activities were launched - // before prev finished? - if (!r.finishing) { - r.frontOfTask = true; - break; - } - } + prevTask.setFrontOfTask(); } else if (prevTask != topTask()) { // This task is going away but it was supposed to return to the home task. // Now the task above it has to return to the home task instead. @@ -1751,9 +1741,9 @@ final class ActivityStack { if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task, new RuntimeException("here").fillInStackTrace()); task.addActivityToTop(r); + task.setFrontOfTask(); r.putInHistory(); - r.frontOfTask = newTask; if (!isHomeStack() || numActivities() > 0) { // We want to show the starting preview window if we are // switching to a new task, or the next activity's process is @@ -2414,15 +2404,12 @@ final class ActivityStack { final ArrayList activities = r.task.mActivities; final int index = activities.indexOf(r); if (index < (activities.size() - 1)) { - ActivityRecord next = activities.get(index+1); - if (r.frontOfTask) { - // The next activity is now the front of the task. - next.frontOfTask = true; - } + r.task.setFrontOfTask(); if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { // If the caller asked that this activity (and all above it) // be cleared when the task is reset, don't lose that information, // but propagate it up to the next activity. + ActivityRecord next = activities.get(index+1); next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); } } diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 483b4a04390e086630510360ea29bb3f18e0c57e..d616f1b66e7ab55b13acdc6643e5e3eb66945d80 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -1134,6 +1134,19 @@ public final class ActivityStackSupervisor { resultRecord.removeResultsLocked( sourceRecord, resultWho, requestCode); } + if (sourceRecord.launchedFromUid == callingUid) { + // The new activity is being launched from the same uid as the previous + // activity in the flow, and asking to forward its result back to the + // previous. In this case the activity is serving as a trampoline between + // the two, so we also want to update its launchedFromPackage to be the + // same as the previous activity. Note that this is safe, since we know + // these two packages come from the same uid; the caller could just as + // well have supplied that same package name itself. This specifially + // deals with the case of an intent picker/chooser being launched in the app + // flow to redirect to an activity picked by the user, where we want the final + // activity to consider it to have been launched by the previous app activity. + callingPackage = sourceRecord.launchedFromPackage; + } } if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) { @@ -1696,6 +1709,7 @@ public final class ActivityStackSupervisor { TaskRecord sourceTask = sourceRecord.task; targetStack = sourceTask.stack; moveHomeStack(targetStack.isHomeStack()); + mWindowManager.moveTaskToTop(sourceTask.taskId); if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { // In this case, we are adding the activity to an existing @@ -1754,6 +1768,7 @@ public final class ActivityStackSupervisor { r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true), null, true); + mWindowManager.moveTaskToTop(r.task.taskId); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new guessed " + r.task); } diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index 3d568ffb8e0722fc488cd3afbeca98e8b4f5e304..9105103ac632524440aa581a55a2f4fbd204499f 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -159,18 +159,33 @@ final class TaskRecord extends ThumbnailHolder { return null; } + /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ + final void setFrontOfTask() { + boolean foundFront = false; + final int numActivities = mActivities.size(); + for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { + final ActivityRecord r = mActivities.get(activityNdx); + if (foundFront || r.finishing) { + r.frontOfTask = false; + } else { + r.frontOfTask = true; + // Set frontOfTask false for every following activity. + foundFront = true; + } + } + } + /** - * Reorder the history stack so that the activity at the given index is - * brought to the front. + * Reorder the history stack so that the passed activity is brought to the front. */ final void moveActivityToFrontLocked(ActivityRecord newTop) { if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop + " to stack at top", new RuntimeException("here").fillInStackTrace()); - getTopActivity().frontOfTask = false; mActivities.remove(newTop); mActivities.add(newTop); - newTop.frontOfTask = true; + + setFrontOfTask(); } void addActivityAtBottom(ActivityRecord r) { diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index 2ca2cc507f2ed4f338ef4265e95d1dfb2a962c54..376414b5e5e83fe03041771e82701cd1ce5609c5 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -45,6 +45,7 @@ import android.net.LinkProperties; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.net.NetworkInfo; +import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.NetworkInfo.DetailedState; import android.os.Binder; @@ -77,6 +78,7 @@ import com.android.server.net.BaseNetworkObserver; import java.io.File; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; import java.net.Inet4Address; import java.net.InetAddress; import java.nio.charset.StandardCharsets; @@ -282,13 +284,12 @@ public class Vpn extends BaseNetworkStateTracker { } /** - * Protect a socket from routing changes by binding it to the given - * interface. The socket is NOT closed by this method. + * Protect a socket from VPN rules by binding it to the main routing table. + * The socket is NOT closed by this method. * * @param socket The socket to be bound. - * @param interfaze The name of the interface. */ - public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception { + public void protect(ParcelFileDescriptor socket) throws Exception { PackageManager pm = mContext.getPackageManager(); int appUid = pm.getPackageUid(mPackage, mUserId); @@ -302,8 +303,6 @@ public class Vpn extends BaseNetworkStateTracker { } finally { Binder.restoreCallingIdentity(token); } - // bind the socket to the interface - jniProtect(socket.getFd(), interfaze); } @@ -353,6 +352,12 @@ public class Vpn extends BaseNetworkStateTracker { Binder.restoreCallingIdentity(token); } + // Save the old config in case we need to go back. + VpnConfig oldConfig = mConfig; + String oldInterface = mInterface; + Connection oldConnection = mConnection; + SparseBooleanArray oldUsers = mVpnUsers; + // Configure the interface. Abort if any of these steps fails. ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); try { @@ -372,12 +377,7 @@ public class Vpn extends BaseNetworkStateTracker { new UserHandle(mUserId))) { throw new IllegalStateException("Cannot bind " + config.user); } - if (mConnection != null) { - mContext.unbindService(mConnection); - } - if (mInterface != null && !mInterface.equals(interfaze)) { - jniReset(mInterface); - } + mConnection = connection; mInterface = interfaze; @@ -386,55 +386,91 @@ public class Vpn extends BaseNetworkStateTracker { config.interfaze = mInterface; config.startTime = SystemClock.elapsedRealtime(); mConfig = config; + // Set up forwarding and DNS rules. mVpnUsers = new SparseBooleanArray(); token = Binder.clearCallingIdentity(); try { mCallback.setMarkedForwarding(mInterface); - mCallback.setRoutes(interfaze, config.routes); + mCallback.setRoutes(mInterface, config.routes); mCallback.override(mInterface, config.dnsServers, config.searchDomains); addVpnUserLocked(mUserId); - + // If we are owner assign all Restricted Users to this VPN + if (mUserId == UserHandle.USER_OWNER) { + for (UserInfo user : mgr.getUsers()) { + if (user.isRestricted()) { + try { + addVpnUserLocked(user.id); + } catch (Exception e) { + Log.wtf(TAG, "Failed to add user " + user.id + " to owner's VPN"); + } + } + } + } } finally { Binder.restoreCallingIdentity(token); } + if (oldConnection != null) { + mContext.unbindService(oldConnection); + } + if (oldInterface != null && !oldInterface.equals(interfaze)) { + // Remove the old tun's user forwarding rules + // The new tun's user rules have already been added so they will take over + // as rules are deleted. This prevents data leakage as the rules are moved over. + token = Binder.clearCallingIdentity(); + try { + final int size = oldUsers.size(); + final boolean forwardDns = (oldConfig.dnsServers != null && + oldConfig.dnsServers.size() != 0); + for (int i = 0; i < size; i++) { + int user = oldUsers.keyAt(i); + mCallback.clearUserForwarding(oldInterface, user, forwardDns); + } + mCallback.clearMarkedForwarding(oldInterface); + } finally { + Binder.restoreCallingIdentity(token); + } + jniReset(oldInterface); + } } catch (RuntimeException e) { updateState(DetailedState.FAILED, "establish"); IoUtils.closeQuietly(tun); // make sure marked forwarding is cleared if it was set + token = Binder.clearCallingIdentity(); try { mCallback.clearMarkedForwarding(mInterface); } catch (Exception ingored) { // ignored + } finally { + Binder.restoreCallingIdentity(token); } + // restore old state + mConfig = oldConfig; + mConnection = oldConnection; + mVpnUsers = oldUsers; + mInterface = oldInterface; throw e; } Log.i(TAG, "Established by " + config.user + " on " + mInterface); - - // If we are owner assign all Restricted Users to this VPN - if (mUserId == UserHandle.USER_OWNER) { - token = Binder.clearCallingIdentity(); - try { - for (UserInfo user : mgr.getUsers()) { - if (user.isRestricted()) { - try { - addVpnUserLocked(user.id); - } catch (Exception e) { - Log.wtf(TAG, "Failed to add user " + user.id + " to owner's VPN"); - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } // TODO: ensure that contract class eventually marks as connected updateState(DetailedState.AUTHENTICATING, "establish"); return tun; } + /** + * Check if a given address is covered by the VPN's routing rules. + */ + public boolean isAddressCovered(InetAddress address) { + synchronized (Vpn.this) { + if (!isRunningLocked()) { + return false; + } + return RouteInfo.selectBestRoute(mConfig.routes, address) != null; + } + } + private boolean isRunningLocked() { return mVpnUsers != null; } @@ -670,7 +706,6 @@ public class Vpn extends BaseNetworkStateTracker { private native int jniSetRoutes(String interfaze, String routes); private native void jniReset(String interfaze); private native int jniCheck(String interfaze); - private native void jniProtect(int socket, String interfaze); private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) { for (RouteInfo route : prop.getAllRoutes()) { diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java index 71d8d9911f9ac0d063631e1721881a0062955f63..9e3dad6ee500051978d7458dacf00984b878c12e 100644 --- a/services/java/com/android/server/content/SyncManager.java +++ b/services/java/com/android/server/content/SyncManager.java @@ -2352,7 +2352,7 @@ public class SyncManager { Log.v(TAG, "canceling and rescheduling sync since an initialization " + "takes higher priority, " + conflict); } - } else if (candidate.expedited && !conflict.mSyncOperation.expedited + } else if (candidate.isExpedited() && !conflict.mSyncOperation.isExpedited() && (candidateIsInitialization == conflict.mSyncOperation.isInitialization())) { toReschedule = conflict; diff --git a/services/java/com/android/server/content/SyncOperation.java b/services/java/com/android/server/content/SyncOperation.java index 485674782aeb9ffe3765c4d77892bf7230061ceb..67e3b098f37c5eadfadc152eb52fcd3a2e17d4ef 100644 --- a/services/java/com/android/server/content/SyncOperation.java +++ b/services/java/com/android/server/content/SyncOperation.java @@ -66,7 +66,8 @@ public class SyncOperation implements Comparable { public final boolean allowParallelSyncs; public Bundle extras; public final String key; - public boolean expedited; + /** Internal boolean to avoid reading a bundle everytime we want to compare operations. */ + private final boolean expedited; public SyncStorageEngine.PendingOperation pendingOperation; /** Elapsed real time in millis at which to run this sync. */ public long latestRunTime; @@ -79,7 +80,7 @@ public class SyncOperation implements Comparable { * Depends on max(backoff, latestRunTime, and delayUntil). */ public long effectiveRunTime; - /** Amount of time before {@link effectiveRunTime} from which this sync can run. */ + /** Amount of time before {@link #effectiveRunTime} from which this sync can run. */ public long flexTime; public SyncOperation(Account account, int userId, int reason, int source, String authority, @@ -98,11 +99,16 @@ public class SyncOperation implements Comparable { this.backoff = backoff; final long now = SystemClock.elapsedRealtime(); // Checks the extras bundle. Must occur after we set the internal bundle. - if (runTimeFromNow < 0 || isExpedited()) { + if (runTimeFromNow < 0) { + // Sanity check: Will always be true. + if (!this.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { + this.extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + } this.expedited = true; this.latestRunTime = now; this.flexTime = 0; } else { + this.extras.remove(ContentResolver.SYNC_EXTRAS_EXPEDITED); this.expedited = false; this.latestRunTime = now + runTimeFromNow; this.flexTime = flexTime; @@ -111,6 +117,24 @@ public class SyncOperation implements Comparable { this.key = toKey(); } + /** Only used to immediately reschedule a sync. */ + SyncOperation(SyncOperation other) { + this.service = other.service; + this.account = other.account; + this.authority = other.authority; + this.userId = other.userId; + this.reason = other.reason; + this.syncSource = other.syncSource; + this.extras = new Bundle(other.extras); + this.expedited = other.expedited; + this.latestRunTime = SystemClock.elapsedRealtime(); + this.flexTime = 0L; + this.backoff = other.backoff; + this.allowParallelSyncs = other.allowParallelSyncs; + this.updateEffectiveRunTime(); + this.key = toKey(); + } + /** * Make sure the bundle attached to this SyncOperation doesn't have unnecessary * flags set. @@ -138,24 +162,6 @@ public class SyncOperation implements Comparable { } } - /** Only used to immediately reschedule a sync. */ - SyncOperation(SyncOperation other) { - this.service = other.service; - this.account = other.account; - this.authority = other.authority; - this.userId = other.userId; - this.reason = other.reason; - this.syncSource = other.syncSource; - this.extras = new Bundle(other.extras); - this.expedited = other.expedited; - this.latestRunTime = SystemClock.elapsedRealtime(); - this.flexTime = 0L; - this.backoff = other.backoff; - this.allowParallelSyncs = other.allowParallelSyncs; - this.updateEffectiveRunTime(); - this.key = toKey(); - } - @Override public String toString() { return dump(null, true); @@ -220,7 +226,7 @@ public class SyncOperation implements Comparable { } public boolean isExpedited() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) || expedited; + return expedited; } public boolean ignoreBackoff() { diff --git a/services/java/com/android/server/content/SyncQueue.java b/services/java/com/android/server/content/SyncQueue.java index 6f3fe6e1d37bc53a641211b0fc11326ecd08351a..22fa2de6c5d6c3817dfefeb5117f5cd2103950ae 100644 --- a/services/java/com/android/server/content/SyncQueue.java +++ b/services/java/com/android/server/content/SyncQueue.java @@ -73,10 +73,9 @@ public class SyncQueue { } SyncOperation syncOperation = new SyncOperation( op.account, op.userId, op.reason, op.syncSource, op.authority, op.extras, - 0 /* delay */, 0 /* flex */, backoff != null ? backoff.first : 0, + op.expedited ? -1: 0 /* delay */, 0 /* flex */, backoff != null ? backoff.first : 0, mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), syncAdapterInfo.type.allowParallelSyncs()); - syncOperation.expedited = op.expedited; syncOperation.pendingOperation = op; add(syncOperation, op); } @@ -104,7 +103,6 @@ public class SyncQueue { if (existingOperation != null) { boolean changed = false; if (operation.compareTo(existingOperation) <= 0 ) { - existingOperation.expedited = operation.expedited; long newRunTime = Math.min(existingOperation.latestRunTime, operation.latestRunTime); // Take smaller runtime. @@ -123,7 +121,7 @@ public class SyncQueue { if (operation.pendingOperation == null) { pop = new SyncStorageEngine.PendingOperation( operation.account, operation.userId, operation.reason, operation.syncSource, - operation.authority, operation.extras, operation.expedited); + operation.authority, operation.extras, operation.isExpedited()); pop = mSyncStorageEngine.insertIntoPending(pop); if (pop == null) { throw new IllegalStateException("error adding pending sync operation " diff --git a/services/java/com/android/server/content/SyncStorageEngine.java b/services/java/com/android/server/content/SyncStorageEngine.java index 124bc60df10c75d8e8dfa68e8fedf035373bd745..178c3723817c4b1cf8afacf1043e2bd7fc704c8d 100644 --- a/services/java/com/android/server/content/SyncStorageEngine.java +++ b/services/java/com/android/server/content/SyncStorageEngine.java @@ -501,7 +501,7 @@ public class SyncStorageEngine extends Handler { * @return amount of seconds before syncTimeSeconds that the sync can occur. * I.e. * earliest_sync_time = syncTimeSeconds - calculateDefaultFlexTime(syncTimeSeconds) - * The flex time is capped at a percentage of the {@link DEFAULT_POLL_FREQUENCY_SECONDS}. + * The flex time is capped at a percentage of the {@link #DEFAULT_POLL_FREQUENCY_SECONDS}. */ public static long calculateDefaultFlexTime(long syncTimeSeconds) { if (syncTimeSeconds < DEFAULT_MIN_FLEX_ALLOWED_SECS) { diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 02f26b375a2a8596f2b1dab0226ed5380b1f3662..bcb677fa455f20ca2252518fb8be1129f5d4a713 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -35,6 +35,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -173,6 +174,9 @@ public final class DisplayManagerService extends IDisplayManager.Stub { // The Wifi display adapter, or null if not registered. private WifiDisplayAdapter mWifiDisplayAdapter; + // The number of active wifi display scan requests. + private int mWifiDisplayScanRequestCount; + // The virtual display adapter, or null if not registered. private VirtualDisplayAdapter mVirtualDisplayAdapter; @@ -458,29 +462,81 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } } - private void onCallbackDied(int pid) { + private void onCallbackDied(CallbackRecord record) { synchronized (mSyncRoot) { - mCallbacks.remove(pid); + mCallbacks.remove(record.mPid); + stopWifiDisplayScanLocked(record); } } @Override // Binder call - public void scanWifiDisplays() { + public void startWifiDisplayScan() { mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, - "Permission required to scan wifi displays"); + "Permission required to start wifi display scans"); + final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { synchronized (mSyncRoot) { + CallbackRecord record = mCallbacks.get(callingPid); + if (record == null) { + throw new IllegalStateException("The calling process has not " + + "registered an IDisplayManagerCallback."); + } + startWifiDisplayScanLocked(record); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void startWifiDisplayScanLocked(CallbackRecord record) { + if (!record.mWifiDisplayScanRequested) { + record.mWifiDisplayScanRequested = true; + if (mWifiDisplayScanRequestCount++ == 0) { if (mWifiDisplayAdapter != null) { - mWifiDisplayAdapter.requestScanLocked(); + mWifiDisplayAdapter.requestStartScanLocked(); } } + } + } + + @Override // Binder call + public void stopWifiDisplayScan() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY, + "Permission required to stop wifi display scans"); + + final int callingPid = Binder.getCallingPid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + CallbackRecord record = mCallbacks.get(callingPid); + if (record == null) { + throw new IllegalStateException("The calling process has not " + + "registered an IDisplayManagerCallback."); + } + stopWifiDisplayScanLocked(record); + } } finally { Binder.restoreCallingIdentity(token); } } + private void stopWifiDisplayScanLocked(CallbackRecord record) { + if (record.mWifiDisplayScanRequested) { + record.mWifiDisplayScanRequested = false; + if (--mWifiDisplayScanRequestCount == 0) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestStopScanLocked(); + } + } else if (mWifiDisplayScanRequestCount < 0) { + Log.wtf(TAG, "mWifiDisplayScanRequestCount became negative: " + + mWifiDisplayScanRequestCount); + mWifiDisplayScanRequestCount = 0; + } + } + } + @Override // Binder call public void connectWifiDisplay(String address) { if (address == null) { @@ -1107,6 +1163,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub { pw.println(" mDefaultViewport=" + mDefaultViewport); pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); + pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); @@ -1134,6 +1191,15 @@ public final class DisplayManagerService extends IDisplayManager.Stub { pw.println(" Display " + displayId + ":"); display.dumpLocked(ipw); } + + final int callbackCount = mCallbacks.size(); + pw.println(); + pw.println("Callbacks: size=" + callbackCount); + for (int i = 0; i < callbackCount; i++) { + CallbackRecord callback = mCallbacks.valueAt(i); + pw.println(" " + i + ": mPid=" + callback.mPid + + ", mWifiDisplayScanRequested=" + callback.mWifiDisplayScanRequested); + } } } @@ -1234,9 +1300,11 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } private final class CallbackRecord implements DeathRecipient { - private final int mPid; + public final int mPid; private final IDisplayManagerCallback mCallback; + public boolean mWifiDisplayScanRequested; + public CallbackRecord(int pid, IDisplayManagerCallback callback) { mPid = pid; mCallback = callback; @@ -1247,7 +1315,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub { if (DEBUG) { Slog.d(TAG, "Display listener for pid " + mPid + " died."); } - onCallbackDied(mPid); + onCallbackDied(this); } public void notifyDisplayEventAsync(int displayId, int event) { diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index fdef0398078d1a96dfe82dbb73399e8bf5339b83..cd57941bcc3edd795be5101d4808c963626de2b8 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -127,7 +127,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate); pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); - + // Try to dump the controller state. if (mDisplayController == null) { pw.println("mDisplayController=null"); @@ -157,43 +157,49 @@ final class WifiDisplayAdapter extends DisplayAdapter { }); } - public void requestScanLocked() { + public void requestStartScanLocked() { if (DEBUG) { - Slog.d(TAG, "requestScanLocked"); + Slog.d(TAG, "requestStartScanLocked"); } getHandler().post(new Runnable() { @Override public void run() { if (mDisplayController != null) { - mDisplayController.requestScan(); + mDisplayController.requestStartScan(); } } }); } - public void requestConnectLocked(final String address) { + public void requestStopScanLocked() { if (DEBUG) { - Slog.d(TAG, "requestConnectLocked: address=" + address); + Slog.d(TAG, "requestStopScanLocked"); } getHandler().post(new Runnable() { @Override public void run() { if (mDisplayController != null) { - mDisplayController.requestConnect(address); + mDisplayController.requestStopScan(); } } }); } - private boolean isRememberedDisplayLocked(String address) { - for (WifiDisplay display : mRememberedDisplays) { - if (display.getDeviceAddress().equals(address)) { - return true; - } + public void requestConnectLocked(final String address) { + if (DEBUG) { + Slog.d(TAG, "requestConnectLocked: address=" + address); } - return false; + + getHandler().post(new Runnable() { + @Override + public void run() { + if (mDisplayController != null) { + mDisplayController.requestConnect(address); + } + } + }); } public void requestPauseLocked() { @@ -552,20 +558,20 @@ final class WifiDisplayAdapter extends DisplayAdapter { } @Override - public void onScanFinished(WifiDisplay[] availableDisplays) { + public void onScanResults(WifiDisplay[] availableDisplays) { synchronized (getSyncRoot()) { availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( availableDisplays); - // check if any of the available displays changed canConnect status boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays); + + // Check whether any of the available displays changed canConnect status. for (int i = 0; !changed && i 0 && mWfdEnabled) { - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (mDiscoverPeersInProgress) { - if (mDiscoverPeersRetriesLeft > 0 && mWfdEnabled) { - mDiscoverPeersRetriesLeft -= 1; - if (DEBUG) { - Slog.d(TAG, "Retrying discovery. Retries left: " - + mDiscoverPeersRetriesLeft); - } - tryDiscoverPeers(); - } else { - handleScanFinished(); - mDiscoverPeersInProgress = false; - } - } - } - }, DISCOVER_PEERS_RETRY_DELAY_MILLIS); - } else { - handleScanFinished(); - mDiscoverPeersInProgress = false; - } + // Ignore the error. + // We will retry automatically in a little bit. + } + }); + + // Retry discover peers periodically until stopped. + mHandler.postDelayed(mDiscoverPeers, DISCOVER_PEERS_INTERVAL_MILLIS); + } + + private void stopPeerDiscovery() { + mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, new ActionListener() { + @Override + public void onSuccess() { + if (DEBUG) { + Slog.d(TAG, "Stop peer discovery succeeded."); + } + } + + @Override + public void onFailure(int reason) { + if (DEBUG) { + Slog.d(TAG, "Stop peer discovery failed with reason " + reason + "."); } } }); @@ -415,7 +449,9 @@ final class WifiDisplayController implements DumpUtils.Dump { } } - handleScanFinished(); + if (mDiscoverPeersInProgress) { + handleScanResults(); + } } }); } @@ -429,7 +465,7 @@ final class WifiDisplayController implements DumpUtils.Dump { }); } - private void handleScanFinished() { + private void handleScanResults() { final int count = mAvailableWifiDisplayPeers.size(); final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count); for (int i = 0; i < count; i++) { @@ -441,7 +477,16 @@ final class WifiDisplayController implements DumpUtils.Dump { mHandler.post(new Runnable() { @Override public void run() { - mListener.onScanFinished(displays); + mListener.onScanResults(displays); + } + }); + } + + private void handleScanFinished() { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onScanFinished(); } }); } @@ -484,6 +529,12 @@ final class WifiDisplayController implements DumpUtils.Dump { return; } + if (!mWfdEnabled) { + Slog.i(TAG, "Ignoring request to connect to Wifi display because the " + +" feature is currently disabled: " + device.deviceName); + return; + } + mDesiredDevice = device; mConnectionRetriesLeft = CONNECT_MAX_RETRIES; updateConnection(); @@ -508,6 +559,10 @@ final class WifiDisplayController implements DumpUtils.Dump { * connection is established (or not). */ private void updateConnection() { + // Step 0. Stop scans if necessary to prevent interference while connected. + // Resume scans later when no longer attempting to connect. + updateScanState(); + // Step 1. Before we try to connect to a new device, tell the system we // have disconnected from the old one. if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) { @@ -661,7 +716,7 @@ final class WifiDisplayController implements DumpUtils.Dump { return; // wait for asynchronous callback } - // Step 6. Listen for incoming connections. + // Step 6. Listen for incoming RTSP connection. if (mConnectedDevice != null && mRemoteDisplay == null) { Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo); if (addr == null) { @@ -817,7 +872,11 @@ final class WifiDisplayController implements DumpUtils.Dump { } } else { mConnectedDeviceGroupInfo = null; - disconnect(); + + // Disconnect if we lost the network while connecting or connected to a display. + if (mConnectingDevice != null || mConnectedDevice != null) { + disconnect(); + } // After disconnection for a group, for some reason we have a tendency // to get a peer change notification with an empty list of peers. @@ -828,6 +887,13 @@ final class WifiDisplayController implements DumpUtils.Dump { } } + private final Runnable mDiscoverPeers = new Runnable() { + @Override + public void run() { + tryDiscoverPeers(); + } + }; + private final Runnable mConnectionTimeout = new Runnable() { @Override public void run() { @@ -1033,7 +1099,8 @@ final class WifiDisplayController implements DumpUtils.Dump { void onFeatureStateChanged(int featureState); void onScanStarted(); - void onScanFinished(WifiDisplay[] availableDisplays); + void onScanResults(WifiDisplay[] availableDisplays); + void onScanFinished(); void onDisplayConnecting(WifiDisplay display); void onDisplayConnectionFailed(); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index a4df576ddce954a38ee8c97aeba1be65b7fd0064..7df70f1da3a8d036acd6e7f4aee94f91f9931143 100755 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -218,6 +218,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final int SCAN_DEFER_DEX = 1<<7; static final int SCAN_BOOTING = 1<<8; static final int SCAN_TRUSTED_OVERLAY = 1<<9; + static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10; static final int REMOVE_CHATTY = 1<<16; @@ -341,7 +342,6 @@ public class PackageManagerService extends IPackageManager.Stub { new HashMap(); // Information for the parser to write more useful error messages. - File mScanningPath; int mLastScanError; // ---------------------------------------------------------------- @@ -883,7 +883,7 @@ public class PackageManagerService extends IPackageManager.Stub { int[] uidArray = new int[] { res.pkg.applicationInfo.uid }; ArrayList pkgList = new ArrayList(1); pkgList.add(res.pkg.applicationInfo.packageName); - sendResourcesChangedBroadcast(true, false, + sendResourcesChangedBroadcast(true, true, pkgList,uidArray, null); } } @@ -4233,7 +4233,6 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK; return null; } - mScanningPath = scanFile; if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; @@ -4253,7 +4252,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (mAndroidApplication != null) { Slog.w(TAG, "*************************************************"); Slog.w(TAG, "Core android package being redefined. Skipping."); - Slog.w(TAG, " file=" + mScanningPath); + Slog.w(TAG, " file=" + scanFile); Slog.w(TAG, "*************************************************"); mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; @@ -4748,6 +4747,10 @@ public class PackageManagerService extends IPackageManager.Stub { if ((scanMode&SCAN_NO_DEX) == 0) { if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { + if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) { + removeDataDirsLI(pkg.packageName); + } + mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } @@ -4825,6 +4828,10 @@ public class PackageManagerService extends IPackageManager.Stub { PackageParser.Package clientPkg = clientLibPkgs.get(i); if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { + if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) { + removeDataDirsLI(pkg.packageName); + } + mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } @@ -9215,7 +9222,7 @@ public class PackageManagerService extends IPackageManager.Stub { replacePackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName, res); } else { - installNewPackageLI(pkg, parseFlags, scanMode, args.user, + installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, res); } synchronized (mPackages) { @@ -10722,7 +10729,8 @@ public class PackageManagerService extends IPackageManager.Stub { DumpState dumpState = new DumpState(); boolean fullPreferred = false; - + boolean checkin = false; + String packageName = null; int opti = 0; @@ -10736,7 +10744,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Right now we only know how to print all. } else if ("-h".equals(opt)) { pw.println("Package manager dump options:"); - pw.println(" [-h] [-f] [cmd] ..."); + pw.println(" [-h] [-f] [--checkin] [cmd] ..."); + pw.println(" --checkin: dump for a checkin"); pw.println(" -f: print details of intent filters"); pw.println(" -h: print this help"); pw.println(" cmd may be one of:"); @@ -10754,13 +10763,15 @@ public class PackageManagerService extends IPackageManager.Stub { pw.println(" : info about given package"); pw.println(" k[eysets]: print known keysets"); return; + } else if ("--checkin".equals(opt)) { + checkin = true; } else if ("-f".equals(opt)) { dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS); } else { pw.println("Unknown argument: " + opt + "; use -h for help"); } } - + // Is the caller requesting to dump a particular piece of data? if (opti < args.length) { String cmd = args[opti]; @@ -10802,17 +10813,26 @@ public class PackageManagerService extends IPackageManager.Stub { } } + if (checkin) { + pw.println("vers,1"); + } + // reader synchronized (mPackages) { if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Verifiers:"); - pw.print(" Required: "); - pw.print(mRequiredVerifierPackage); - pw.print(" (uid="); - pw.print(getPackageUid(mRequiredVerifierPackage, 0)); - pw.println(")"); + if (!checkin) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Verifiers:"); + pw.print(" Required: "); + pw.print(mRequiredVerifierPackage); + pw.print(" (uid="); + pw.print(getPackageUid(mRequiredVerifierPackage, 0)); + pw.println(")"); + } else if (mRequiredVerifierPackage != null) { + pw.print("vrfy,"); pw.print(mRequiredVerifierPackage); + pw.print(","); pw.println(getPackageUid(mRequiredVerifierPackage, 0)); + } } if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { @@ -10821,21 +10841,37 @@ public class PackageManagerService extends IPackageManager.Stub { while (it.hasNext()) { String name = it.next(); SharedLibraryEntry ent = mSharedLibraries.get(name); - if (!printedHeader) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Libraries:"); - printedHeader = true; + if (!checkin) { + if (!printedHeader) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Libraries:"); + printedHeader = true; + } + pw.print(" "); + } else { + pw.print("lib,"); } - pw.print(" "); pw.print(name); - pw.print(" -> "); + if (!checkin) { + pw.print(" -> "); + } if (ent.path != null) { - pw.print("(jar) "); - pw.print(ent.path); + if (!checkin) { + pw.print("(jar) "); + pw.print(ent.path); + } else { + pw.print(",jar,"); + pw.print(ent.path); + } } else { - pw.print("(apk) "); - pw.print(ent.apk); + if (!checkin) { + pw.print("(apk) "); + pw.print(ent.apk); + } else { + pw.print(",apk,"); + pw.print(ent.apk); + } } pw.println(); } @@ -10844,16 +10880,22 @@ public class PackageManagerService extends IPackageManager.Stub { if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { if (dumpState.onTitlePrinted()) pw.println(); - pw.println("Features:"); + if (!checkin) { + pw.println("Features:"); + } Iterator it = mAvailableFeatures.keySet().iterator(); while (it.hasNext()) { String name = it.next(); - pw.print(" "); + if (!checkin) { + pw.print(" "); + } else { + pw.print("feat,"); + } pw.println(name); } } - if (dumpState.isDumping(DumpState.DUMP_RESOLVERS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) { if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:" : "Activity Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { @@ -10876,7 +10918,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - if (dumpState.isDumping(DumpState.DUMP_PREFERRED)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { for (int i=0; i users) { + void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag, PackageSetting ps, + SimpleDateFormat sdf, Date date, List users) { + if (checkinTag != null) { + pw.print(checkinTag); + pw.print(","); + pw.print(ps.realName != null ? ps.realName : ps.name); + pw.print(","); + pw.print(ps.appId); + pw.print(","); + pw.print(ps.versionCode); + pw.print(","); + pw.print(ps.firstInstallTime); + pw.print(","); + pw.print(ps.lastUpdateTime); + pw.print(","); + pw.print(ps.installerPackageName != null ? ps.installerPackageName : "?"); + pw.println(); + for (UserInfo user : users) { + pw.print(checkinTag); + pw.print("-"); + pw.print("usr"); + pw.print(","); + pw.print(user.id); + pw.print(","); + pw.print(ps.getInstalled(user.id) ? "I" : "i"); + pw.print(ps.getBlocked(user.id) ? "B" : "b"); + pw.print(ps.getStopped(user.id) ? "S" : "s"); + pw.print(ps.getNotLaunched(user.id) ? "l" : "L"); + pw.print(","); + pw.print(ps.getEnabled(user.id)); + String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); + pw.print(","); + pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?"); + pw.println(); + } + return; + } + pw.print(prefix); pw.print("Package ["); pw.print(ps.realName != null ? ps.realName : ps.name); pw.print("] ("); @@ -3072,7 +3108,7 @@ final class Settings { } } - void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) { + void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) { final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); final Date date = new Date(); boolean printedSomething = false; @@ -3083,35 +3119,39 @@ final class Settings { continue; } - if (packageName != null) { + if (!checkin && packageName != null) { dumpState.setSharedUser(ps.sharedUser); } - if (!printedSomething) { + if (!checkin && !printedSomething) { if (dumpState.onTitlePrinted()) pw.println(); pw.println("Packages:"); printedSomething = true; } - dumpPackageLPr(pw, " ", ps, sdf, date, users); + dumpPackageLPr(pw, " ", checkin ? "pkg" : null, ps, sdf, date, users); } printedSomething = false; - if (mRenamedPackages.size() > 0) { + if (!checkin && mRenamedPackages.size() > 0) { for (final Map.Entry e : mRenamedPackages.entrySet()) { if (packageName != null && !packageName.equals(e.getKey()) && !packageName.equals(e.getValue())) { continue; } - if (!printedSomething) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Renamed packages:"); - printedSomething = true; + if (!checkin) { + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Renamed packages:"); + printedSomething = true; + } + pw.print(" "); + } else { + pw.print("ren,"); } - pw.print(" "); pw.print(e.getKey()); - pw.print(" -> "); + pw.print(checkin ? " -> " : ","); pw.println(e.getValue()); } } @@ -3123,13 +3163,13 @@ final class Settings { && !packageName.equals(ps.name)) { continue; } - if (!printedSomething) { + if (!checkin && !printedSomething) { if (dumpState.onTitlePrinted()) pw.println(); pw.println("Hidden system packages:"); printedSomething = true; } - dumpPackageLPr(pw, " ", ps, sdf, date, users); + dumpPackageLPr(pw, " ", checkin ? "dis" : null, ps, sdf, date, users); } } } diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 60d44c78e5b6a496e802a0125352900fa45b033e..30bc9226da24fe881171a149721a91503936b683 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -610,7 +610,6 @@ final class DisplayPowerController { && mProximity == PROXIMITY_POSITIVE) { mScreenOffBecauseOfProximity = true; sendOnProximityPositiveWithWakelock(); - setScreenOn(false); } } else if (mWaitingForNegativeProximity && mScreenOffBecauseOfProximity @@ -670,59 +669,62 @@ final class DisplayPowerController { mUsingScreenAutoBrightness = false; } - // Animate the screen on or off. - if (!mScreenOffBecauseOfProximity) { - if (wantScreenOn(mPowerRequest.screenState)) { - // Want screen on. - // Wait for previous off animation to complete beforehand. - // It is relatively short but if we cancel it and switch to the - // on animation immediately then the results are pretty ugly. - if (!mElectronBeamOffAnimator.isStarted()) { - // Turn the screen on. The contents of the screen may not yet - // be visible if the electron beam has not been dismissed because - // its last frame of animation is solid black. - setScreenOn(true); - - if (mPowerRequest.blockScreenOn - && mPowerState.getElectronBeamLevel() == 0.0f) { - blockScreenOn(); - } else { - unblockScreenOn(); - if (USE_ELECTRON_BEAM_ON_ANIMATION) { - if (!mElectronBeamOnAnimator.isStarted()) { - if (mPowerState.getElectronBeamLevel() == 1.0f) { - mPowerState.dismissElectronBeam(); - } else if (mPowerState.prepareElectronBeam( - mElectronBeamFadesConfig ? - ElectronBeam.MODE_FADE : - ElectronBeam.MODE_WARM_UP)) { - mElectronBeamOnAnimator.start(); - } else { - mElectronBeamOnAnimator.end(); - } + // Animate the screen on or off unless blocked. + if (mScreenOffBecauseOfProximity) { + // Screen off due to proximity. + setScreenOn(false); + unblockScreenOn(); + } else if (wantScreenOn(mPowerRequest.screenState)) { + // Want screen on. + // Wait for previous off animation to complete beforehand. + // It is relatively short but if we cancel it and switch to the + // on animation immediately then the results are pretty ugly. + if (!mElectronBeamOffAnimator.isStarted()) { + // Turn the screen on. The contents of the screen may not yet + // be visible if the electron beam has not been dismissed because + // its last frame of animation is solid black. + setScreenOn(true); + + if (mPowerRequest.blockScreenOn + && mPowerState.getElectronBeamLevel() == 0.0f) { + blockScreenOn(); + } else { + unblockScreenOn(); + if (USE_ELECTRON_BEAM_ON_ANIMATION) { + if (!mElectronBeamOnAnimator.isStarted()) { + if (mPowerState.getElectronBeamLevel() == 1.0f) { + mPowerState.dismissElectronBeam(); + } else if (mPowerState.prepareElectronBeam( + mElectronBeamFadesConfig ? + ElectronBeam.MODE_FADE : + ElectronBeam.MODE_WARM_UP)) { + mElectronBeamOnAnimator.start(); + } else { + mElectronBeamOnAnimator.end(); } - } else { - mPowerState.setElectronBeamLevel(1.0f); - mPowerState.dismissElectronBeam(); } + } else { + mPowerState.setElectronBeamLevel(1.0f); + mPowerState.dismissElectronBeam(); } } - } else { - // Want screen off. - // Wait for previous on animation to complete beforehand. - if (!mElectronBeamOnAnimator.isStarted()) { - if (!mElectronBeamOffAnimator.isStarted()) { - if (mPowerState.getElectronBeamLevel() == 0.0f) { - setScreenOn(false); - } else if (mPowerState.prepareElectronBeam( - mElectronBeamFadesConfig ? - ElectronBeam.MODE_FADE : - ElectronBeam.MODE_COOL_DOWN) - && mPowerState.isScreenOn()) { - mElectronBeamOffAnimator.start(); - } else { - mElectronBeamOffAnimator.end(); - } + } + } else { + // Want screen off. + // Wait for previous on animation to complete beforehand. + unblockScreenOn(); + if (!mElectronBeamOnAnimator.isStarted()) { + if (!mElectronBeamOffAnimator.isStarted()) { + if (mPowerState.getElectronBeamLevel() == 0.0f) { + setScreenOn(false); + } else if (mPowerState.prepareElectronBeam( + mElectronBeamFadesConfig ? + ElectronBeam.MODE_FADE : + ElectronBeam.MODE_COOL_DOWN) + && mPowerState.isScreenOn()) { + mElectronBeamOffAnimator.start(); + } else { + mElectronBeamOffAnimator.end(); } } } @@ -762,15 +764,15 @@ final class DisplayPowerController { private void unblockScreenOn() { if (mScreenOnWasBlocked) { mScreenOnWasBlocked = false; - if (DEBUG) { - Slog.d(TAG, "Unblocked screen on after " + - (SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime) + " ms"); + long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime; + if (delay > 1000 || DEBUG) { + Slog.d(TAG, "Unblocked screen on after " + delay + " ms"); } } } private void setScreenOn(boolean on) { - if (!mPowerState.isScreenOn() == on) { + if (mPowerState.isScreenOn() != on) { mPowerState.setScreenOn(on); if (on) { mNotifier.onScreenOn(); diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java index fa318f8b619892293e16e4a1f5f2035ef557c79d..5c048f150a455b32e07cacffca2dea3d3dc74bca 100644 --- a/services/java/com/android/server/power/DisplayPowerState.java +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -304,8 +304,15 @@ final class DisplayPowerState { int brightness = mScreenOn && mElectronBeamLevel > 0f ? mScreenBrightness : 0; if (mPhotonicModulator.setState(mScreenOn, brightness)) { + if (DEBUG) { + Slog.d(TAG, "Screen ready"); + } mScreenReady = true; invokeCleanListenerIfNeeded(); + } else { + if (DEBUG) { + Slog.d(TAG, "Screen not ready"); + } } } }; @@ -355,7 +362,7 @@ final class DisplayPowerState { AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask); } } - return mChangeInProgress; + return !mChangeInProgress; } } diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index da9548f7266e0b82eec897d214e5ed83166aa918..134718b38c96b52f3cf9bdc865f43a5289eee725 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -1135,6 +1135,9 @@ public final class PowerManagerService extends IPowerManager.Stub if (!mSystemReady || mDirty == 0) { return; } + if (!Thread.holdsLock(mLock)) { + Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked"); + } // Phase 0: Basic state updates. updateIsPoweredLocked(mDirty); diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 5f075175a1e22e84d8b4d4a0b42d08b128939400..b9326325dc007321ec9b29ff6eea7d95d94f6912 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -98,6 +98,13 @@ public class UsbDeviceManager { // which need debouncing. private static final int UPDATE_DELAY = 1000; + // Time we received a request to enter USB accessory mode + private long mAccessoryModeRequestTime = 0; + + // Timeout for entering USB request mode. + // Request is cancelled if host does not configure device within 10 seconds. + private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000; + private static final String BOOT_MODE_PROPERTY = "ro.bootmode"; private UsbHandler mHandler; @@ -205,6 +212,8 @@ public class UsbDeviceManager { } private void startAccessoryMode() { + if (!mHasUsbAccessory) return; + mAccessoryStrings = nativeGetAccessoryStrings(); boolean enableAudio = (nativeGetAudioMode() == AUDIO_MODE_SOURCE); // don't start accessory mode if our mandatory strings have not been set @@ -223,6 +232,7 @@ public class UsbDeviceManager { } if (functions != null) { + mAccessoryModeRequestTime = SystemClock.elapsedRealtime(); setCurrentFunctions(functions, false); } } @@ -456,6 +466,8 @@ public class UsbDeviceManager { } private void setEnabledFunctions(String functions, boolean makeDefault) { + if (DEBUG) Slog.d(TAG, "setEnabledFunctions " + functions + + " makeDefault: " + makeDefault); // Do not update persystent.sys.usb.config if the device is booted up // with OEM specific mode. @@ -517,9 +529,16 @@ public class UsbDeviceManager { } private void updateCurrentAccessory() { - if (!mHasUsbAccessory) return; + // We are entering accessory mode if we have received a request from the host + // and the request has not timed out yet. + boolean enteringAccessoryMode = + mAccessoryModeRequestTime > 0 && + SystemClock.elapsedRealtime() < + mAccessoryModeRequestTime + ACCESSORY_REQUEST_TIMEOUT; + + if (mConfigured && enteringAccessoryMode) { + // successfully entered accessory mode - if (mConfigured) { if (mAccessoryStrings != null) { mCurrentAccessory = new UsbAccessory(mAccessoryStrings); Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); @@ -530,7 +549,7 @@ public class UsbDeviceManager { } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); } - } else if (!mConnected) { + } else if (!enteringAccessoryMode) { // make sure accessory mode is off // and restore default functions Slog.d(TAG, "exited USB accessory mode"); @@ -560,6 +579,8 @@ public class UsbDeviceManager { } } + if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " connected: " + mConnected + + " configured: " + mConfigured); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -604,9 +625,7 @@ public class UsbDeviceManager { if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); - } - - if (!mConnected) { + } else if (!mConnected) { // restore defaults when USB is disconnected setEnabledFunctions(mDefaultFunctions, false); } diff --git a/services/java/com/android/server/wifi/WifiController.java b/services/java/com/android/server/wifi/WifiController.java index a3d514e304ca9ebb758d4cf6fb4cb60e8d860964..bdb0a5e27f4976f43784c49c6ec67a468bfabd2b 100644 --- a/services/java/com/android/server/wifi/WifiController.java +++ b/services/java/com/android/server/wifi/WifiController.java @@ -152,11 +152,21 @@ class WifiController extends StateMachine { addState(mStaDisabledWithScanState, mDefaultState); addState(mApEnabledState, mDefaultState); addState(mEcmState, mDefaultState); - if (mSettingsStore.isScanAlwaysAvailable()) { + + boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); + boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); + boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); + + log("isAirplaneModeOn = " + isAirplaneModeOn + + ", isWifiEnabled = " + isWifiEnabled + + ", isScanningAvailable = " + isScanningAlwaysAvailable); + + if (isWifiEnabled && isScanningAlwaysAvailable) { setInitialState(mStaDisabledWithScanState); } else { setInitialState(mApStaDisabledState); } + setLogRecSize(100); setLogOnlyTransitions(false); diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index f2efde10156572d156517022bac4b1b0c44f901f..aecf9aea5b6a9376835d06a20dc42b3d464cff2b 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -870,7 +870,7 @@ public final class WifiService extends IWifiManager.Stub { public void setCountryCode(String countryCode, boolean persist) { Slog.i(TAG, "WifiService trying to set country code to " + countryCode + " with persist set to " + persist); - enforceChangePermission(); + enforceConnectivityInternalPermission(); final long token = Binder.clearCallingIdentity(); try { mWifiStateMachine.setCountryCode(countryCode, persist); diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index b1d67decacb6eec62f9ef6e946c042c2d3c3ee68..e98014bdea3e9579f5c1647b47b3310e634effec 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -53,7 +53,7 @@ class AppWindowToken extends WindowToken { int groupId = -1; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - int configChanges; + boolean layoutConfigChanges; boolean showWhenLocked; // The input dispatching timeout for this application token in nanoseconds. diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java index e65aecb035ed9fb5f2fd7fc5c145373f78cbf0fb..cb29df47487435fbc3c3bc5d96340a1e4f4d074f 100644 --- a/services/java/com/android/server/wm/TaskStack.java +++ b/services/java/com/android/server/wm/TaskStack.java @@ -271,6 +271,8 @@ public class TaskStack { for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { final WindowState win = windows.get(winNdx); if (!resizingWindows.contains(win)) { + if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG, + "setBounds: Resizing " + win); resizingWindows.add(win); } win.mUnderStatusBar = underStatusBar; diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index b6c677093c9326e52981ea8d574a4d7e0b4d16de..096921d13eba0598574d03577fa9a1a57eb25f7d 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -437,8 +437,15 @@ public class WindowManagerService extends IWindowManager.Stub int mRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; - ArrayList mRotationWatchers - = new ArrayList(); + class RotationWatcher { + IRotationWatcher watcher; + IBinder.DeathRecipient dr; + RotationWatcher(IRotationWatcher w, IBinder.DeathRecipient d) { + watcher = w; + dr = d; + } + } + ArrayList mRotationWatchers = new ArrayList(); int mDeferredRotationPauseCount; int mSystemDecorLayer = 0; @@ -3444,7 +3451,8 @@ public class WindowManagerService extends IWindowManager.Stub atoken.appFullscreen = fullscreen; atoken.showWhenLocked = showWhenLocked; atoken.requestedOrientation = requestedOrientation; - atoken.configChanges = configChanges; + atoken.layoutConfigChanges = (configChanges & + (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + " to stack=" + stackId + " task=" + taskId + " at " + addPos); @@ -5992,7 +6000,7 @@ public class WindowManagerService extends IWindowManager.Stub for (int i=mRotationWatchers.size()-1; i>=0; i--) { try { - mRotationWatchers.get(i).onRotationChanged(rotation); + mRotationWatchers.get(i).watcher.onRotationChanged(rotation); } catch (RemoteException e) { } } @@ -6024,10 +6032,10 @@ public class WindowManagerService extends IWindowManager.Stub public void binderDied() { synchronized (mWindowMap) { for (int i=0; iGetStringUTFChars(jName, NULL) : NULL; - if (!name) { - jniThrowNullPointerException(env, "name"); - return; - } - if (bind_to_interface(socket, name) < 0) { - throwException(env, SYSTEM_ERROR, "Cannot protect socket"); - } - env->ReleaseStringUTFChars(jName, name); -} - //------------------------------------------------------------------------------ static JNINativeMethod gMethods[] = { @@ -455,7 +433,6 @@ static JNINativeMethod gMethods[] = { {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes}, {"jniReset", "(Ljava/lang/String;)V", (void *)reset}, {"jniCheck", "(Ljava/lang/String;)I", (void *)check}, - {"jniProtect", "(ILjava/lang/String;)V", (void *)protect}, }; int register_android_server_connectivity_Vpn(JNIEnv *env) diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java index 37176d6d31f481d8eea9cb8937b83373bf9910e8..910b6855dc212b23183741ceb99e54f125ae4876 100644 --- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java @@ -136,10 +136,11 @@ public class SyncOperationTest extends AndroidTestCase { "authority1", b1, soon + 100, soonFlex + 100, unimportant, unimportant, true); assertTrue(op1.compareTo(op2) == -1); - assertTrue("less than not transitive.", op2.compareTo(op1) == 1); - assertTrue(op1.compareTo(op3) == 1); - assertTrue("greater than not transitive. ", op3.compareTo(op1) == -1); - assertTrue("overlapping intervals not the same.", op1.compareTo(op4) == 0); - assertTrue("equality not transitive.", op4.compareTo(op1) == 0); + assertTrue("Less than not transitive.", op2.compareTo(op1) == 1); + assertEquals("Expedited not smaller than non-expedited.", -1, op1.compareTo(op3)); + assertEquals("Greater than not transitive for expedited. ", 1, op3.compareTo(op1)); + assertTrue("overlapping intervals not compared based on start interval.", + op1.compareTo(op4) == -1); + assertTrue("overlapping interval comparison not transitive.", op4.compareTo(op1) == 1); } } diff --git a/telephony/java/android/telephony/CallStateListener.java b/telephony/java/android/telephony/CallStateListener.java new file mode 100644 index 0000000000000000000000000000000000000000..e2ffbfa55be07221d9d69c0d05af1dab1651abd7 --- /dev/null +++ b/telephony/java/android/telephony/CallStateListener.java @@ -0,0 +1,36 @@ +/* + * 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 android.telephony; + +import android.annotation.PrivateApi; + +/** @hide */ +@PrivateApi +public interface CallStateListener { + /** + * Notify of a new or updated call. + * Any time the state of a call is updated, it will alert any listeners. This includes changes + * of state such as when a call is put on hold or conferenced. + * + * @param callId a unique ideCntifier for a given call that can be used to track state changes + * @param state the new state of the call. + * {@see com.android.services.telephony.common.Call$State} + * @param number the phone number of the call. For some states, this may be blank. However, it + * will be populated for any initial state. + */ + public void onCallStateChanged(int callId, int state, String number); +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 35d63c068f447ceb4ae4cd6b1e0b8888e9e4c7ca..710b6279ab9e458d65606d5bb85ed053ddac3150 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -16,17 +16,22 @@ package android.telephony; +import android.annotation.PrivateApi; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.telephony.Rlog; +import android.util.Log; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.ITelephonyListener; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; @@ -34,6 +39,7 @@ import com.android.internal.telephony.TelephonyProperties; import java.io.FileInputStream; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -60,8 +66,41 @@ public class TelephonyManager { private static final String TAG = "TelephonyManager"; private static ITelephonyRegistry sRegistry; + + private final HashMap mListeners + = new HashMap(); private final Context mContext; + private static class Listener extends ITelephonyListener.Stub { + final CallStateListener mListener; + private static final int WHAT = 1; + + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + mListener.onCallStateChanged(msg.arg1, msg.arg2, (String)msg.obj); + } + }; + + Listener(CallStateListener listener) { + mListener = listener; + } + + @Override + public void onUpdate(final int callId, final int state, final String number) { + if (mHandler != null) { + mHandler.sendMessage(mHandler.obtainMessage(WHAT, callId, state, number)); + } + } + + void clearQueue() { + mHandler.removeMessages(WHAT); + + // Don't accept more incoming binder calls either. + mHandler = null; + } + } + /** @hide */ public TelephonyManager(Context context) { Context appContext = context.getApplicationContext(); @@ -1708,4 +1747,397 @@ public class TelephonyManager { return mContext.getResources().getString( com.android.internal.R.string.config_mms_user_agent_profile_url); } + + /** @hide */ + @PrivateApi + public void dial(String number) { + try { + getITelephony().dial(number); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#dial", e); + } + } + + /** @hide */ + @PrivateApi + public void call(String callingPackage, String number) { + try { + getITelephony().call(callingPackage, number); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#call", e); + } + } + + /** @hide */ + @PrivateApi + public boolean showCallScreen() { + try { + return getITelephony().showCallScreen(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#showCallScreen", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean showCallScreenWithDialpad(boolean showDialpad) { + try { + return getITelephony().showCallScreenWithDialpad(showDialpad); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#showCallScreenWithDialpad", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean endCall() { + try { + return getITelephony().endCall(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#endCall", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public void answerRingingCall() { + try { + getITelephony().answerRingingCall(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#answerRingingCall", e); + } + } + + /** @hide */ + @PrivateApi + public void toggleHold() { + try { + getITelephony().toggleHold(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#toggleHold", e); + } + } + + /** @hide */ + @PrivateApi + public void merge() { + try { + getITelephony().merge(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#merge", e); + } + } + + /** @hide */ + @PrivateApi + public void swap() { + try { + getITelephony().swap(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#swap", e); + } + } + + /** @hide */ + @PrivateApi + public void mute(boolean mute) { + try { + getITelephony().mute(mute); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#mute", e); + } + } + + /** @hide */ + @PrivateApi + public void silenceRinger() { + try { + getITelephony().silenceRinger(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#silenceRinger", e); + } + } + + /** @hide */ + @PrivateApi + public boolean isOffhook() { + try { + return getITelephony().isOffhook(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isOffhook", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean isRinging() { + try { + return getITelephony().isRinging(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isRinging", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean isIdle() { + try { + return getITelephony().isIdle(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isIdle", e); + } + return true; + } + + /** @hide */ + @PrivateApi + public boolean isRadioOn() { + try { + return getITelephony().isRadioOn(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isRadioOn", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean isSimPinEnabled() { + try { + return getITelephony().isSimPinEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isSimPinEnabled", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public void cancelMissedCallsNotification() { + try { + getITelephony().cancelMissedCallsNotification(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#cancelMissedCallsNotification", e); + } + } + + /** @hide */ + @PrivateApi + public boolean supplyPin(String pin) { + try { + return getITelephony().supplyPin(pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPin", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean supplyPuk(String puk, String pin) { + try { + return getITelephony().supplyPuk(puk, pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPuk", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public int[] supplyPinReportResult(String pin) { + try { + return getITelephony().supplyPinReportResult(pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPinReportResult", e); + } + return new int[0]; + } + + /** @hide */ + @PrivateApi + public int[] supplyPukReportResult(String puk, String pin) { + try { + return getITelephony().supplyPukReportResult(puk, pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#]", e); + } + return new int[0]; + } + + /** @hide */ + @PrivateApi + public boolean handlePinMmi(String dialString) { + try { + return getITelephony().handlePinMmi(dialString); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#handlePinMmi", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public void toggleRadioOnOff() { + try { + getITelephony().toggleRadioOnOff(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#toggleRadioOnOff", e); + } + } + + /** @hide */ + @PrivateApi + public boolean setRadio(boolean turnOn) { + try { + return getITelephony().setRadio(turnOn); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setRadio", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean setRadioPower(boolean turnOn) { + try { + return getITelephony().setRadioPower(turnOn); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setRadioPower", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public void updateServiceLocation() { + try { + getITelephony().updateServiceLocation(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e); + } + } + + /** @hide */ + @PrivateApi + public int enableApnType(String type) { + try { + return getITelephony().enableApnType(type); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#enableApnType", e); + } + return PhoneConstants.APN_REQUEST_FAILED; + } + + /** @hide */ + @PrivateApi + public int disableApnType(String type) { + try { + return getITelephony().disableApnType(type); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#disableApnType", e); + } + return PhoneConstants.APN_REQUEST_FAILED; + } + + /** @hide */ + @PrivateApi + public boolean enableDataConnectivity() { + try { + return getITelephony().enableDataConnectivity(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#enableDataConnectivity", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean disableDataConnectivity() { + try { + return getITelephony().disableDataConnectivity(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#disableDataConnectivity", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean isDataConnectivityPossible() { + try { + return getITelephony().isDataConnectivityPossible(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isDataConnectivityPossible", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public boolean needsOtaServiceProvisioning() { + try { + return getITelephony().needsOtaServiceProvisioning(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#needsOtaServiceProvisioning", e); + } + return false; + } + + /** @hide */ + @PrivateApi + public void playDtmfTone(char digit, boolean timedShortCode) { + try { + getITelephony().playDtmfTone(digit, timedShortCode); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#playDtmfTone", e); + } + } + + /** @hide */ + @PrivateApi + public void stopDtmfTone() { + try { + getITelephony().stopDtmfTone(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#stopDtmfTone", e); + } + } + + /** @hide */ + @PrivateApi + public void addCallStateListener(CallStateListener listener) { + try { + if (listener == null) { + throw new RuntimeException("Listener can't be null"); + } + if (!mListeners.containsKey(listener)) { + final Listener l = new Listener(listener); + mListeners.put(listener, l); + getITelephony().addListener(l); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#addListener", e); + } + } + + /** @hide */ + @PrivateApi + public void removeCallStateListener(CallStateListener listener) { + try { + final Listener l = mListeners.remove(listener); + if (l != null) { + // Make sure that no callbacks that are already in flight come. + l.clearQueue(); + getITelephony().removeListener(l); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#removeListener", e); + } + } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 7bd2c8412e97953f269c656b72029e8fdafc998b..9c73059f7a7c0099452500fda4705af36202b13f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -17,9 +17,12 @@ package com.android.internal.telephony; import android.os.Bundle; -import java.util.List; -import android.telephony.NeighboringCellInfo; import android.telephony.CellInfo; +import android.telephony.NeighboringCellInfo; + +import com.android.internal.telephony.ITelephonyListener; + +import java.util.List; /** * Interface used to interact with the phone. Mostly this is used by the @@ -324,5 +327,48 @@ interface ITelephony { * Sets minimum time in milli-seconds between onCellInfoChanged */ void setCellInfoListRate(int rateInMillis); + + /** + * Put a call on hold. + */ + void toggleHold(); + + /** + * Merge foreground and background calls. + */ + void merge(); + + /** + * Swap foreground and background calls. + */ + void swap(); + + /** + * Mute the phone. + */ + void mute(boolean mute); + + /** + * Start playing DTMF tone for the specified digit. + * + * @param digit the digit that corresponds with the desired tone. + * @param timedShortcode whether the specified digit should be played as a timed short code. + */ + void playDtmfTone(char digit, boolean timedShortCode); + + /** + * Stop playing DTMF tones. + */ + void stopDtmfTone(); + + /** + * Register a callback. + */ + void addListener(ITelephonyListener listener); + + /** + * Unregister a callback. + */ + void removeListener(ITelephonyListener listener); } diff --git a/telephony/java/com/android/internal/telephony/ITelephonyListener.aidl b/telephony/java/com/android/internal/telephony/ITelephonyListener.aidl new file mode 100644 index 0000000000000000000000000000000000000000..c2262172a3c7c6e16de300f0967b0719230189b8 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ITelephonyListener.aidl @@ -0,0 +1,27 @@ +/* + * 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.internal.telephony; + +/** + * Interface used to register a listener that gets more detailed call state information than + * {@link android.telephony.PhoneStateListener} + * + * {@hide} + */ +oneway interface ITelephonyListener { + void onUpdate(int callId, int state, String number); +} diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java index 91d04da05930b3c42c7426a6259a745d13d9132b..db80ef951570ebccbbe63ac82f91e342e2b94d7d 100644 --- a/test-runner/src/android/test/InstrumentationTestRunner.java +++ b/test-runner/src/android/test/InstrumentationTestRunner.java @@ -74,8 +74,9 @@ import junit.textui.ResultPrinter; *

  • {@link android.test.ProviderTestCase}
  • *
  • {@link android.test.ServiceTestCase}
  • *
  • {@link android.test.SingleLaunchActivityTestCase}
- *
  • In an appropriate AndroidManifest.xml, define the this instrumentation with - * the appropriate android:targetPackage set. + *
  • Set the android:targetPackage attribute of the <instrumentation> + * element in the test package's manifest. You should set the attribute value + * to the package name of the target application under test. *
  • Run the instrumentation using "adb shell am instrument -w", * with no optional arguments, to run all tests (except performance tests). *
  • Run the instrumentation using "adb shell am instrument -w", diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 62f6affd9b3a9ba38de40d46069276af1e2901eb..dfb80707f14c023a49eee2ad1edee2cd8709da45 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -85,11 +85,13 @@ public class AppLaunch extends InstrumentationTestCase { // do initial app launch, without force stopping for (String app : mNameToResultKey.keySet()) { long launchTime = startApp(app, false); - if (launchTime <=0 ) { + if (launchTime <= 0) { mNameToLaunchTime.put(app, -1L); // simply pass the app if launch isn't successful // error should have already been logged by startApp continue; + } else { + mNameToLaunchTime.put(app, launchTime); } sleep(INITIAL_LAUNCH_IDLE_TIMEOUT); closeApp(app, false); @@ -98,9 +100,9 @@ public class AppLaunch extends InstrumentationTestCase { // do the real app launch now for (int i = 0; i < mLaunchIterations; i++) { for (String app : mNameToResultKey.keySet()) { - long totalLaunchTime = mNameToLaunchTime.get(app); + long prevLaunchTime = mNameToLaunchTime.get(app); long launchTime = 0; - if (totalLaunchTime < 0) { + if (prevLaunchTime < 0) { // skip if the app has previous failures continue; } @@ -110,18 +112,19 @@ public class AppLaunch extends InstrumentationTestCase { mNameToLaunchTime.put(app, -1L); continue; } - totalLaunchTime += launchTime; - mNameToLaunchTime.put(app, totalLaunchTime); + // keep the min launch time + if (launchTime < prevLaunchTime) { + mNameToLaunchTime.put(app, launchTime); + } sleep(POST_LAUNCH_IDLE_TIMEOUT); closeApp(app, true); sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); } } for (String app : mNameToResultKey.keySet()) { - long totalLaunchTime = mNameToLaunchTime.get(app); - if (totalLaunchTime != -1) { - mResult.putDouble(mNameToResultKey.get(app), - ((double) totalLaunchTime) / mLaunchIterations); + long launchTime = mNameToLaunchTime.get(app); + if (launchTime != -1) { + mResult.putLong(mNameToResultKey.get(app), launchTime); } } instrumentation.sendStatus(0, mResult); diff --git a/tests/LegacyRestoreTest/README b/tests/LegacyRestoreTest/README new file mode 100644 index 0000000000000000000000000000000000000000..cdd157e58295bab43fd36db5037bd8b842f88bab --- /dev/null +++ b/tests/LegacyRestoreTest/README @@ -0,0 +1,18 @@ +The file "jbmr2-encrypted-settings-abcd.ab" in this directory is an encrypted +"adb backup" archive of the settings provider package. It was generated on a +Nexus 4 running Android 4.3 (API 18), and so predates the Android 4.4 changes +to the PBKDF2 implementation. The archive's encryption password, entered on-screen, +is "abcd" (with no quotation marks). + +'adb restore' decrypts and applies the restored archive successfully on a device +running Android 4.3, but fails to restore correctly on a device running Android 4.4, +reporting an invalid password in logcat. This is the situation reported in bug +. + +The file "kk-fixed-encrypted-settings-abcd.ab" is a similar encrypted "adb backup" +archive, using the same key, generated on a Nexus 4 running Android 4.4 with a fix +to this bug in place. This archive should be successfully restorable on any +version of Android which incorporates the fix. + +These archives can be used as an ongoing test to verify that historical encrypted +archives from various points in Android's history can be successfully restored. diff --git a/tests/LegacyRestoreTest/jbmr2-encrypted-settings-abcd.ab b/tests/LegacyRestoreTest/jbmr2-encrypted-settings-abcd.ab new file mode 100644 index 0000000000000000000000000000000000000000..192dcf5d4c52847bd28c52878b9e0d4983ab61e5 Binary files /dev/null and b/tests/LegacyRestoreTest/jbmr2-encrypted-settings-abcd.ab differ diff --git a/tests/LegacyRestoreTest/kk-fixed-encrypted-settings-abcd.ab b/tests/LegacyRestoreTest/kk-fixed-encrypted-settings-abcd.ab new file mode 100644 index 0000000000000000000000000000000000000000..bf2b558d49b9910b3a50518a716bbc4a239f0aa7 Binary files /dev/null and b/tests/LegacyRestoreTest/kk-fixed-encrypted-settings-abcd.ab differ diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index ed497a5c0f34d8432442928ac2a310ccddb8e4a0..1fa9615f6620612bf9026cfdfa629fad21598944 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -33,6 +33,8 @@ built_core_classes := $(call java-lib-files,core) built_ext_dep := $(call java-lib-deps,ext) built_ext_classes := $(call java-lib-files,ext) +built_ext_data := $(call intermediates-dir-for, \ + JAVA_LIBRARIES,ext,,COMMON)/javalib.jar built_layoutlib_create_jar := $(call intermediates-dir-for, \ JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar @@ -60,7 +62,8 @@ $(LOCAL_BUILT_MODULE): $(built_core_dep) \ $@ \ $(built_core_classes) \ $(built_framework_classes) \ - $(built_ext_classes) + $(built_ext_classes) \ + $(built_ext_data) $(hide) ls -l $(built_framework_classes) diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java index 62d0a0d6ae9df452bf2d5b57f0313d01d701babb..802cf1c70828b3cbc8bdc60e095c9588d0305a9e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -20,6 +20,7 @@ import java.awt.Font; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; +import java.awt.geom.Rectangle2D; import java.util.LinkedList; import java.util.List; @@ -50,9 +51,12 @@ public class BidiRenderer { } } - /* package */ Graphics2D graphics; - /* package */ Paint_Delegate paint; - /* package */ char[] text; + private Graphics2D mGraphics; + private Paint_Delegate mPaint; + private char[] mText; + // Bounds of the text drawn so far. + private RectF mBounds; + private float mBaseline; /** * @param graphics May be null. @@ -61,9 +65,9 @@ public class BidiRenderer { */ /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { assert (paint != null); - this.graphics = graphics; - this.paint = paint; - this.text = text; + mGraphics = graphics; + mPaint = paint; + mText = text; } /** @@ -77,61 +81,62 @@ public class BidiRenderer { * @param advances If not null, then advances for each character to be rendered are returned * here. * @param advancesIndex index into advances from where the advances need to be filled. - * @param draw If true and {@link graphics} is not null, draw the rendered text on the graphics + * @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics * at the given co-ordinates * @param x The x-coordinate of the left edge of where the text should be drawn on the given * graphics. - * @param y The y-coordinate at which to draw the text on the given graphics. - * @return The x-coordinate of the right edge of the drawn text. In other words, - * x + the width of the text. + * @param y The y-coordinate at which to draw the text on the given mGraphics. + * @return A rectangle specifying the bounds of the text drawn. */ - /* package */ float renderText(int start, int limit, boolean isRtl, float advances[], + /* package */ RectF renderText(int start, int limit, boolean isRtl, float[] advances, int advancesIndex, boolean draw, float x, float y) { // We break the text into scripts and then select font based on it and then render each of // the script runs. - for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) { + mBounds = new RectF(x, y, x, y); + mBaseline = y; + for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) { int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT; flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT; - x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw, - x, y); + renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw); advancesIndex += run.limit - run.start; } - return x; + return mBounds; } /** - * Render a script run. Use the preferred font to render as much as possible. This also - * implements a fallback mechanism to render characters that cannot be drawn using the - * preferred font. - * - * @return x + width of the text drawn. + * Render a script run to the right of the bounds passed. Use the preferred font to render as + * much as possible. This also implements a fallback mechanism to render characters that cannot + * be drawn using the preferred font. */ - private float renderScript(int start, int limit, FontInfo preferredFont, int flag, - float advances[], int advancesIndex, boolean draw, float x, float y) { - List fonts = paint.getFonts(); + private void renderScript(int start, int limit, FontInfo preferredFont, int flag, + float[] advances, int advancesIndex, boolean draw) { + List fonts = mPaint.getFonts(); if (fonts == null || preferredFont == null) { - return x; + return; } while (start < limit) { boolean foundFont = false; - int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit); + int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit); if (canDisplayUpTo == -1) { - return render(start, limit, preferredFont, flag, advances, advancesIndex, draw, - x, y); - } else if (canDisplayUpTo > start) { // can draw something - x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, - draw, x, y); + // We can draw all characters in the text. + render(start, limit, preferredFont, flag, advances, advancesIndex, draw); + return; + } + if (canDisplayUpTo > start) { + // We can draw something. + render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw); advancesIndex += canDisplayUpTo - start; start = canDisplayUpTo; } - int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1; + // The current character cannot be drawn with the preferred font. Cycle through all the + // fonts to check which one can draw it. + int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1; for (FontInfo font : fonts) { - canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount); + canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount); if (canDisplayUpTo == -1) { - x = render(start, start+charCount, font, flag, advances, advancesIndex, draw, - x, y); + render(start, start+charCount, font, flag, advances, advancesIndex, draw); start += charCount; advancesIndex += charCount; foundFont = true; @@ -143,46 +148,63 @@ public class BidiRenderer { // probably appear as a box or a blank space. We could, probably, use some // heuristics and break the character into the base character and diacritics and // then draw it, but it's probably not worth the effort. - x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex, - draw, x, y); + render(start, start + charCount, preferredFont, flag, advances, advancesIndex, + draw); start += charCount; advancesIndex += charCount; } } - return x; } /** - * Render the text with the given font. + * Renders the text to the right of the bounds with the given font. + * @param font The font to render the text with. */ - private float render(int start, int limit, FontInfo font, int flag, float advances[], - int advancesIndex, boolean draw, float x, float y) { + private void render(int start, int limit, FontInfo font, int flag, float[] advances, + int advancesIndex, boolean draw) { - float totalAdvance = 0; // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with // the anti-aliasing set. FontRenderContext f = font.mMetrics.getFontRenderContext(); - FontRenderContext frc = new FontRenderContext(f.getTransform(), paint.isAntiAliased(), + FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(), f.usesFractionalMetrics()); - GlyphVector gv = font.mFont.layoutGlyphVector(frc, text, start, limit, flag); + GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag); int ng = gv.getNumGlyphs(); int[] ci = gv.getGlyphCharIndices(0, ng, null); - for (int i = 0; i < ng; i++) { - float adv = gv.getGlyphMetrics(i).getAdvanceX(); - if (advances != null) { + if (advances != null) { + for (int i = 0; i < ng; i++) { int adv_idx = advancesIndex + ci[i]; - advances[adv_idx] += adv; + advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX(); } - totalAdvance += adv; } - if (draw && graphics != null) { - graphics.drawGlyphVector(gv, x, y); + if (draw && mGraphics != null) { + mGraphics.drawGlyphVector(gv, mBounds.right, mBaseline); + } + + // Update the bounds. + Rectangle2D awtBounds = gv.getLogicalBounds(); + RectF bounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline); + // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the + // coordinates from the bounds as an offset. + if (Math.abs(mBounds.right - mBounds.left) == 0) { + mBounds = bounds; + } else { + mBounds.union(bounds); } - return x + totalAdvance; } // --- Static helper methods --- + private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY) { + float left = (float) awtRec.getX(); + float top = (float) awtRec.getY(); + float right = (float) (left + awtRec.getWidth()); + float bottom = (float) (top + awtRec.getHeight()); + RectF androidRect = new RectF(left, top, right, bottom); + androidRect.offset(offsetX, offsetY); + return androidRect; + } + /* package */ static List getScriptRuns(char[] text, int start, int limit, boolean isRtl, List fonts) { LinkedList scriptRuns = new LinkedList(); diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 3111f0d1977c0b281ff4ae9001e96ea7e7801010..73d274c1d32b07df03c4787843392625edf1e49b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -989,7 +989,8 @@ public final class Canvas_Delegate { int limit = index + count; boolean isRtl = flags == Canvas.DIRECTION_RTL; if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { - float m = paintDelegate.measureText(text, index, count, isRtl); + RectF bounds = paintDelegate.measureText(text, index, count, isRtl); + float m = bounds.right - bounds.left; if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { x -= m / 2; } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index 38745ce65bf2952fb6794bab05a77ce76680dae6..74b2893ae4950ee53552ebcf30f692dff67b6437 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -176,7 +176,7 @@ public final class NinePatch_Delegate { /*package*/ static void nativeDraw(long canvas_instance, RectF loc, long bitmap_instance, long chunk, long paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, - (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(), + (int) loc.left, (int) loc.top, (int) loc.right, (int) loc.bottom, bitmap_instance, chunk, paint_instance_or_null, destDensity, srcDensity); } @@ -185,7 +185,7 @@ public final class NinePatch_Delegate { /*package*/ static void nativeDraw(long canvas_instance, Rect loc, long bitmap_instance, long chunk, long paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, - loc.left, loc.top, loc.width(), loc.height(), + loc.left, loc.top, loc.right, loc.bottom, bitmap_instance, chunk, paint_instance_or_null, destDensity, srcDensity); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index ca8e8aa4e1c256050db664385df10ae9174c5bce..7007b7187407a0a55769fc731281e97a469aa890 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -575,7 +575,8 @@ public class Paint_Delegate { return 0; } - return delegate.measureText(text, index, count, isRtl(bidiFlags)); + RectF bounds = delegate.measureText(text, index, count, isRtl(bidiFlags)); + return bounds.right - bounds.left; } @LayoutlibDelegate @@ -614,7 +615,8 @@ public class Paint_Delegate { } // measure from start to end - float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); + RectF bounds = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); + float res = bounds.right - bounds.left; if (measuredWidth != null) { measuredWidth[measureIndex] = res; @@ -991,8 +993,9 @@ public class Paint_Delegate { boolean isRtl = isRtl(flags); int limit = index + count; - return new BidiRenderer(null, delegate, text).renderText( + RectF bounds = new BidiRenderer(null, delegate, text).renderText( index, limit, isRtl, advances, advancesIndex, false, 0, 0); + return bounds.right - bounds.left; } @LayoutlibDelegate @@ -1058,9 +1061,7 @@ public class Paint_Delegate { if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return; } - int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags)); - int h= delegate.getFonts().get(0).mMetrics.getHeight(); - bounds.set(0, 0, w, h); + delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds); } @LayoutlibDelegate @@ -1154,7 +1155,7 @@ public class Paint_Delegate { } } - /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) { + /*package*/ RectF measureText(char[] text, int index, int count, boolean isRtl) { return new BidiRenderer(null, this, text).renderText( index, index + count, isRtl, null, 0, false, 0, 0); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index a25fb59d114f8ca07d816bead2758f161430ae3c..60cd157e3379ad5bb4bf84a797147bf2c210a4f3 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -103,6 +103,9 @@ public final class Typeface_Delegate { if (familyName == null) { familyName = DEFAULT_FAMILY; } + if (style < 0) { + style = Typeface.NORMAL; + } Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style); if (sFontLoader != null) { diff --git a/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java index 15cd68789863162c433e4a17792dd77d97c4df47..320dd0dcb664b014fc929f511d376937fd9e2236 100644 --- a/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java @@ -17,6 +17,7 @@ package android.text.format; import java.util.Calendar; +import java.util.TimeZone; import java.util.UnknownFormatConversionException; import java.util.regex.Pattern; @@ -35,6 +36,28 @@ public class Time_Delegate { // Regex to match odd number of '%'. private static final Pattern p = Pattern.compile("(?"; + + @LayoutlibDelegate + /*package*/ static long normalize(Time thisTime, boolean ignoreDst) { + long millis = toMillis(thisTime, ignoreDst); + set(thisTime, millis); + return millis; + } + + @LayoutlibDelegate + /*package*/ static void switchTimezone(Time thisTime, String timezone) { + Calendar c = timeToCalendar(thisTime); + c.setTimeZone(TimeZone.getTimeZone(timezone)); + calendarToTime(c, thisTime); + } + + @LayoutlibDelegate + /*package*/ static int nativeCompare(Time a, Time b) { + return timeToCalendar(a).compareTo(timeToCalendar(b)); + } + @LayoutlibDelegate /*package*/ static String format1(Time thisTime, String format) { @@ -46,16 +69,92 @@ public class Time_Delegate { // of $. return String.format( p.matcher(format).replaceAll("$0\\1\\$t"), - timeToCalendar(thisTime, Calendar.getInstance())); + timeToCalendar(thisTime)); } catch (UnknownFormatConversionException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_STRFTIME, "Unrecognized format", e, format); return format; } } - private static Calendar timeToCalendar(Time time, Calendar calendar) { + /** + * Return the current time in YYYYMMDDTHHMMSS format + */ + @LayoutlibDelegate + /*package*/ static String toString(Time thisTime) { + Calendar c = timeToCalendar(thisTime); + return String.format(FORMAT, c); + } + + @LayoutlibDelegate + /*package*/ static boolean nativeParse(Time thisTime, String s) { + Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, + "android.text.format.Time.parse() not supported.", null); + return false; + } + + @LayoutlibDelegate + /*package*/ static boolean nativeParse3339(Time thisTime, String s) { + Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, + "android.text.format.Time.parse3339() not supported.", null); + return false; + } + + @LayoutlibDelegate + /*package*/ static void setToNow(Time thisTime) { + calendarToTime(getCalendarInstance(thisTime), thisTime); + } + + @LayoutlibDelegate + /*package*/ static long toMillis(Time thisTime, boolean ignoreDst) { + // TODO: Respect ignoreDst. + return timeToCalendar(thisTime).getTimeInMillis(); + } + + @LayoutlibDelegate + /*package*/ static void set(Time thisTime, long millis) { + Calendar c = getCalendarInstance(thisTime); + c.setTimeInMillis(millis); + calendarToTime(c,thisTime); + } + + @LayoutlibDelegate + /*package*/ static String format2445(Time thisTime) { + Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, + "android.text.format.Time.format2445() not supported.", null); + return ""; + } + + // ---- private helper methods ---- + + private static Calendar timeToCalendar(Time time) { + Calendar calendar = getCalendarInstance(time); calendar.set(time.year, time.month, time.monthDay, time.hour, time.minute, time.second); return calendar; } + private static void calendarToTime(Calendar c, Time time) { + time.timezone = c.getTimeZone().getID(); + time.set(c.get(Calendar.SECOND), c.get(Calendar.MINUTE), c.get(Calendar.HOUR_OF_DAY), + c.get(Calendar.DATE), c.get(Calendar.MONTH), c.get(Calendar.YEAR)); + time.weekDay = c.get(Calendar.DAY_OF_WEEK); + time.yearDay = c.get(Calendar.DAY_OF_YEAR); + time.isDst = c.getTimeZone().inDaylightTime(c.getTime()) ? 1 : 0; + // gmtoff is in seconds and TimeZone.getOffset() returns milliseconds. + time.gmtoff = c.getTimeZone().getOffset(c.getTimeInMillis()) / DateUtils.SECOND_IN_MILLIS; + } + + /** + * Return a calendar instance with the correct timezone. + * + * @param time Time to obtain the timezone from. + */ + private static Calendar getCalendarInstance(Time time) { + // TODO: Check platform code to make sure the behavior is same for null/invalid timezone. + if (time == null || time.timezone == null) { + // Default to local timezone. + return Calendar.getInstance(); + } + // If timezone is invalid, use GMT. + return Calendar.getInstance(TimeZone.getTimeZone(time.timezone)); + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java index 108b651e302c382e8d2885e7122672986c3aed07..cc7338ae8c324f876642d91109c4981643cdf7d6 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java @@ -57,7 +57,9 @@ public final class FontLoader { private static final String FONT_SUFFIX_NONE = ".ttf"; private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf"; private static final String FONT_SUFFIX_BOLD = "-Bold.ttf"; - private static final String FONT_SUFFIX_ITALIC = "-Italic.ttf"; + // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked + // separately. + private static final String FONT_SUFFIX_ITALIC = "Italic.ttf"; private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf"; // This must match the values of Typeface styles so that we can use them for indices in this @@ -285,10 +287,10 @@ public final class FontLoader { mFontInfo.font[Typeface.NORMAL] = font; } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) { mFontInfo.font[Typeface.BOLD] = font; - } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) { - mFontInfo.font[Typeface.ITALIC] = font; } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) { mFontInfo.font[Typeface.BOLD_ITALIC] = font; + } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) { + mFontInfo.font[Typeface.ITALIC] = font; } else if (fileName.endsWith(FONT_SUFFIX_NONE)) { mFontInfo.font[Typeface.NORMAL] = font; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 57771e326a2b5d343852562298e608809e167dea..377d99664fb7a10b0c2088146b88f1261f66ab2f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -334,7 +334,7 @@ public class RenderSessionImpl extends RenderAction { backgroundView = backgroundLayout; backgroundLayout.setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + LayoutParams.MATCH_PARENT, 0); layoutParams.weight = 1; backgroundLayout.setLayoutParams(layoutParams); topLayout.addView(backgroundLayout); @@ -369,7 +369,7 @@ public class RenderSessionImpl extends RenderAction { // content frame mContentRoot = new FrameLayout(context); layoutParams = new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + LayoutParams.MATCH_PARENT, 0); layoutParams.weight = 1; mContentRoot.setLayoutParams(layoutParams); backgroundLayout.addView(mContentRoot); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java index 1572a4034432ae078faa148fb19d6ea366a6ac93..9a31705d0a3a95a6c183679e9104e5e3ac6a9463 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java @@ -29,6 +29,7 @@ import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureVisitor; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -60,6 +61,9 @@ public class AsmAnalyzer { private final String[] mIncludeGlobs; /** The set of classes to exclude.*/ private final Set mExcludedClasses; + /** Glob patterns of files to keep as is. */ + private final String[] mIncludeFileGlobs; + /** Copy these files into the output as is. */ /** * Creates a new analyzer. @@ -70,15 +74,19 @@ public class AsmAnalyzer { * @param deriveFrom Keep all classes that derive from these one (these included). * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*" * ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is) + * @param includeFileGlobs Glob patterns of files which are kept as is. This is only for files + * not ending in .class. */ public AsmAnalyzer(Log log, List osJarPath, AsmGenerator gen, - String[] deriveFrom, String[] includeGlobs, Set excludeClasses) { + String[] deriveFrom, String[] includeGlobs, Set excludeClasses, + String[] includeFileGlobs) { mLog = log; mGen = gen; mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList(); mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0]; mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0]; mExcludedClasses = excludeClasses; + mIncludeFileGlobs = includeFileGlobs != null ? includeFileGlobs : new String[0]; } /** @@ -86,7 +94,11 @@ public class AsmAnalyzer { * Fills the generator with classes & dependencies found. */ public void analyze() throws IOException, LogAbortException { - Map zipClasses = parseZip(mOsSourceJar); + + TreeMap zipClasses = new TreeMap(); + Map filesFound = new TreeMap(); + + parseZip(mOsSourceJar, zipClasses, filesFound); mLog.info("Found %d classes in input JAR%s.", zipClasses.size(), mOsSourceJar.size() > 1 ? "s" : ""); @@ -96,15 +108,29 @@ public class AsmAnalyzer { if (mGen != null) { mGen.setKeep(found); mGen.setDeps(deps); + mGen.setCopyFiles(filesFound); } } /** - * Parses a JAR file and returns a list of all classes founds using a map - * class name => ASM ClassReader. Class names are in the form "android.view.View". + * Parses a JAR file and adds all the classes found to classes + * and all other files to filesFound. + * + * @param classes The map of class name => ASM ClassReader. Class names are + * in the form "android.view.View". + * @param fileFound The map of file name => InputStream. The file name is + * in the form "android/data/dataFile". */ - Map parseZip(List jarPathList) throws IOException { - TreeMap classes = new TreeMap(); + void parseZip(List jarPathList, Map classes, + Map filesFound) throws IOException { + if (classes == null || filesFound == null) { + return; + } + + Pattern[] includeFilePatterns = new Pattern[mIncludeFileGlobs.length]; + for (int i = 0; i < mIncludeFileGlobs.length; ++i) { + includeFilePatterns[i] = getPatternFromGlob(mIncludeFileGlobs[i]); + } for (String jarPath : jarPathList) { ZipFile zip = new ZipFile(jarPath); @@ -116,11 +142,17 @@ public class AsmAnalyzer { ClassReader cr = new ClassReader(zip.getInputStream(entry)); String className = classReaderToClassName(cr); classes.put(className, cr); + } else { + for (int i = 0; i < includeFilePatterns.length; ++i) { + if (includeFilePatterns[i].matcher(entry.getName()).matches()) { + filesFound.put(entry.getName(), zip.getInputStream(entry)); + break; + } + } } } } - return classes; } /** @@ -202,7 +234,19 @@ public class AsmAnalyzer { */ void findGlobs(String globPattern, Map zipClasses, Map inOutFound) throws LogAbortException { - // transforms the glob pattern in a regexp: + + Pattern regexp = getPatternFromGlob(globPattern); + + for (Entry entry : zipClasses.entrySet()) { + String class_name = entry.getKey(); + if (regexp.matcher(class_name).matches()) { + findClass(class_name, zipClasses, inOutFound); + } + } + } + + Pattern getPatternFromGlob(String globPattern) { + // transforms the glob pattern in a regexp: // - escape "." with "\." // - replace "*" by "[^.]*" // - escape "$" with "\$" @@ -216,14 +260,7 @@ public class AsmAnalyzer { globPattern = globPattern.replaceAll("@", ".*"); globPattern += "$"; - Pattern regexp = Pattern.compile(globPattern); - - for (Entry entry : zipClasses.entrySet()) { - String class_name = entry.getKey(); - if (regexp.matcher(class_name).matches()) { - findClass(class_name, zipClasses, inOutFound); - } - } + return Pattern.compile(globPattern); } /** diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index b10256127ae880ca4a97194bf88372e6ae878fbb..207d8ae7b2d9eb5d536803c9b35b53ae96140636 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -20,6 +20,7 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; +import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -52,6 +53,8 @@ public class AsmGenerator { private Map mKeep; /** All dependencies that must be completely stubbed. */ private Map mDeps; + /** All files that are to be copied as-is. */ + private Map mCopyFiles; /** Counter of number of classes renamed during transform. */ private int mRenameCount; /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */ @@ -195,6 +198,11 @@ public class AsmGenerator { mDeps = deps; } + /** Sets the map of files to output as-is. */ + public void setCopyFiles(Map copyFiles) { + mCopyFiles = copyFiles; + } + /** Gets the map of classes to output as-is, except if they have native methods */ public Map getKeep() { return mKeep; @@ -205,6 +213,11 @@ public class AsmGenerator { return mDeps; } + /** Gets the map of files to output as-is. */ + public Map getCopyFiles() { + return mCopyFiles; + } + /** Generates the final JAR */ public void generate() throws FileNotFoundException, IOException { TreeMap all = new TreeMap(); @@ -232,6 +245,15 @@ public class AsmGenerator { all.put(name, b); } + for (Entry entry : mCopyFiles.entrySet()) { + try { + byte[] b = inputStreamToByteArray(entry.getValue()); + all.put(entry.getKey(), b); + } catch (IOException e) { + // Ignore. + } + + } mLog.info("# deps classes: %d", mDeps.size()); mLog.info("# keep classes: %d", mKeep.size()); mLog.info("# renamed : %d", mRenameCount); @@ -381,4 +403,13 @@ public class AsmGenerator { return cv.hasNativeMethods(); } + private byte[] inputStreamToByteArray(InputStream is) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] data = new byte[8192]; // 8KB + int n; + while ((n = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, n); + } + return buffer.toByteArray(); + } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index f6779e38ccfa625227fe08ff4e055e86b354b695..79aa642b7024c7f649ef45cf5b819457a81ef57f 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -131,7 +131,6 @@ public final class CreateInfo implements ICreateInfo { "android.os.HandlerThread#run", "android.os.Build#getString", "android.text.format.DateFormat#is24HourFormat", - "android.text.format.Time#format1", "android.view.Choreographer#getRefreshRate", "android.view.Display#updateDisplayInfoLocked", "android.view.LayoutInflater#rInflate", @@ -188,6 +187,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.Xfermode", "android.os.SystemClock", "android.text.AndroidBidi", + "android.text.format.Time", "android.util.FloatMath", "android.view.Display", "libcore.icu.DateIntervalFormat", diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index ee501d234d01f1987654a49c364c6ceba75a71d2..a79fba19d2161a27cb64c784ae5765c91b047730 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -115,7 +115,10 @@ public class Main { "android.database.ContentObserver", // for Digital clock "com.android.i18n.phonenumbers.*", // for TextView with autolink attribute }, - excludeClasses); + excludeClasses, + new String[] { + "com/android/i18n/phonenumbers/data/*", + }); aa.analyze(); agen.generate(); diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java index 005fc9dadab417608942f1a49474e83910d660d7..7ec0d389be872b7a7c8a6c13a9ddb74d32d10078 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java @@ -29,6 +29,7 @@ import org.junit.Test; import org.objectweb.asm.ClassReader; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; @@ -55,8 +56,10 @@ public class AsmAnalyzerTest { Set excludeClasses = new HashSet(1); excludeClasses.add("java.lang.JavaClass"); - mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */, - null /* deriveFrom */, null /* includeGlobs */, excludeClasses); + + String[] includeFiles = new String[]{"mock_android/data/data*"}; + mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */, null /* deriveFrom */, + null /* includeGlobs */, excludeClasses, includeFiles); } @After @@ -65,7 +68,11 @@ public class AsmAnalyzerTest { @Test public void testParseZip() throws IOException { - Map map = mAa.parseZip(mOsJarPath); + + Map map = new TreeMap(); + Map filesFound = new TreeMap(); + + mAa.parseZip(mOsJarPath, map, filesFound); assertArrayEquals(new String[] { "java.lang.JavaClass", @@ -86,11 +93,17 @@ public class AsmAnalyzerTest { "mock_android.widget.TableLayout$LayoutParams" }, map.keySet().toArray()); + assertArrayEquals(new String[] {"mock_android/data/dataFile"}, + filesFound.keySet().toArray()); } @Test public void testFindClass() throws IOException, LogAbortException { - Map zipClasses = mAa.parseZip(mOsJarPath); + + Map zipClasses = new TreeMap(); + Map filesFound = new TreeMap(); + + mAa.parseZip(mOsJarPath, zipClasses, filesFound); TreeMap found = new TreeMap(); ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams", @@ -105,7 +118,11 @@ public class AsmAnalyzerTest { @Test public void testFindGlobs() throws IOException, LogAbortException { - Map zipClasses = mAa.parseZip(mOsJarPath); + + Map zipClasses = new TreeMap(); + Map filesFound = new TreeMap(); + + mAa.parseZip(mOsJarPath, zipClasses, filesFound); TreeMap found = new TreeMap(); // this matches classes, a package match returns nothing @@ -164,7 +181,11 @@ public class AsmAnalyzerTest { @Test public void testFindClassesDerivingFrom() throws LogAbortException, IOException { - Map zipClasses = mAa.parseZip(mOsJarPath); + + Map zipClasses = new TreeMap(); + Map filesFound = new TreeMap(); + + mAa.parseZip(mOsJarPath, zipClasses, filesFound); TreeMap found = new TreeMap(); mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found); @@ -186,7 +207,11 @@ public class AsmAnalyzerTest { @Test public void testDependencyVisitor() throws IOException, LogAbortException { - Map zipClasses = mAa.parseZip(mOsJarPath); + + Map zipClasses = new TreeMap(); + Map filesFound = new TreeMap(); + + mAa.parseZip(mOsJarPath, zipClasses, filesFound); TreeMap keep = new TreeMap(); TreeMap new_keep = new TreeMap(); TreeMap in_deps = new TreeMap(); diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java index 8a27173181a309c5b7718c08eff21e59d9729568..0dbc2387b12871555046fa9fe414df362b9fdc39 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java @@ -33,6 +33,7 @@ import org.objectweb.asm.Type; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; @@ -131,7 +132,8 @@ public class AsmGeneratorTest { new String[] { // include classes "**" }, - new HashSet(0) /* excluded classes */); + new HashSet(0) /* excluded classes */, + new String[]{} /* include files */); aa.analyze(); agen.generate(); @@ -195,10 +197,15 @@ public class AsmGeneratorTest { new String[] { // include classes "**" }, - new HashSet(1)); + new HashSet(1), + new String[] { /* include files */ + "mock_android/data/data*" + }); aa.analyze(); agen.generate(); - Map output = parseZip(mOsDestJar); + Map output = new TreeMap(); + Map filesFound = new TreeMap(); + parseZip(mOsDestJar, output, filesFound); boolean injectedClassFound = false; for (ClassReader cr: output.values()) { TestClassVisitor cv = new TestClassVisitor(); @@ -206,10 +213,13 @@ public class AsmGeneratorTest { injectedClassFound |= cv.mInjectedClassFound; } assertTrue(injectedClassFound); + assertArrayEquals(new String[] {"mock_android/data/dataFile"}, + filesFound.keySet().toArray()); } - private Map parseZip(String jarPath) throws IOException { - TreeMap classes = new TreeMap(); + private void parseZip(String jarPath, + Map classes, + Map filesFound) throws IOException { ZipFile zip = new ZipFile(jarPath); Enumeration entries = zip.entries(); @@ -220,10 +230,11 @@ public class AsmGeneratorTest { ClassReader cr = new ClassReader(zip.getInputStream(entry)); String className = classReaderToClassName(cr); classes.put(className, cr); + } else { + filesFound.put(entry.getName(), zip.getInputStream(entry)); } } - return classes; } private String classReaderToClassName(ClassReader classReader) { diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar index 60d8efb1bb997982776ffa3a8b9a6824de644b60..8dd04812a866a6ed1677c039695686857275776e 100644 Binary files a/tools/layoutlib/create/tests/data/mock_android.jar and b/tools/layoutlib/create/tests/data/mock_android.jar differ diff --git a/tools/layoutlib/create/tests/mock_data/mock_android/data/anotherDataFile b/tools/layoutlib/create/tests/mock_data/mock_android/data/anotherDataFile new file mode 100644 index 0000000000000000000000000000000000000000..ab29fbe449cf92aca8d4bc81509ddefd42a13e8f --- /dev/null +++ b/tools/layoutlib/create/tests/mock_data/mock_android/data/anotherDataFile @@ -0,0 +1 @@ +A simple data file that should *not* be copied to the output jar. \ No newline at end of file diff --git a/tools/layoutlib/create/tests/mock_data/mock_android/data/dataFile b/tools/layoutlib/create/tests/mock_data/mock_android/data/dataFile new file mode 100644 index 0000000000000000000000000000000000000000..9b01893ebab37291ab1536d2f23b23e58c8e69f1 --- /dev/null +++ b/tools/layoutlib/create/tests/mock_data/mock_android/data/dataFile @@ -0,0 +1 @@ +A simple data file that should be copied to the output jar unchanged. \ No newline at end of file diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 69d3efed1ca6b818978f0981d8f50fd1eceb0083..0fffd373b1423af33d605dfd29b0728f470ca983 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -233,6 +233,10 @@ public class WifiStateMachine extends StateMachine { private DhcpStateMachine mDhcpStateMachine; private boolean mDhcpActive = false; + // Delay in switching to null country code (non-null has no delay) + private final int COUNTRY_CODE_DELAY_MS = 15000; + private final AtomicInteger mCountryCodeSequence = new AtomicInteger(); + private class InterfaceObserver extends BaseNetworkObserver { private WifiStateMachine mWifiStateMachine; @@ -1535,14 +1539,18 @@ public class WifiStateMachine extends StateMachine { * @param persist {@code true} if the setting should be remembered. */ public void setCountryCode(String countryCode, boolean persist) { - if (persist) { - mPersistedCountryCode = countryCode; - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.WIFI_COUNTRY_CODE, - countryCode); + // If it's a country code, apply immediately, + // If it's empty, delay it in case it's a momentary dropout + int countryCodeSequence = mCountryCodeSequence.incrementAndGet(); + if (TextUtils.isEmpty(countryCode)) { + String defaultCountryCode = mContext.getResources().getString( + R.string.config_wifi_unknown_country_code); + + sendMessageDelayed(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, + defaultCountryCode, COUNTRY_CODE_DELAY_MS); + } else { + sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode); } - sendMessage(CMD_SET_COUNTRY_CODE, countryCode); - mWifiP2pChannel.sendMessage(WifiP2pService.SET_COUNTRY_CODE, countryCode); } /** @@ -2508,7 +2516,9 @@ public class WifiStateMachine extends StateMachine { // to the driver happened between mPersistedCountryCode getting set // and now, so simply persisting it here would mean we have sent // nothing to the driver. Send the cmd so it might be set now. - sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode); + int sequenceNum = mCountryCodeSequence.incrementAndGet(); + sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, + sequenceNum, 0, countryCode); } break; case CMD_SET_BATCHED_SCAN: @@ -3086,23 +3096,38 @@ public class WifiStateMachine extends StateMachine { case CMD_SET_BATCHED_SCAN: if (recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj)) { - startBatchedScan(); + if (mBatchedScanSettings != null) { + startBatchedScan(); + } else { + stopBatchedScan(); + } } break; case CMD_SET_COUNTRY_CODE: String country = (String) message.obj; + final boolean persist = (message.arg2 == 1); + final int sequence = message.arg1; + if (sequence != mCountryCodeSequence.get()) { + if (DBG) log("set country code ignored due to sequence num"); + break; + } if (DBG) log("set country code " + country); - if (country != null) { - country = country.toUpperCase(Locale.ROOT); - if (mLastSetCountryCode == null - || country.equals(mLastSetCountryCode) == false) { - if (mWifiNative.setCountryCode(country)) { - mLastSetCountryCode = country; - } else { - loge("Failed to set country code " + country); - } + if (persist) { + mPersistedCountryCode = country; + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.WIFI_COUNTRY_CODE, + country); + } + country = country.toUpperCase(Locale.ROOT); + if (mLastSetCountryCode == null + || country.equals(mLastSetCountryCode) == false) { + if (mWifiNative.setCountryCode(country)) { + mLastSetCountryCode = country; + } else { + loge("Failed to set country code " + country); } } + mWifiP2pChannel.sendMessage(WifiP2pService.SET_COUNTRY_CODE, country); break; case CMD_SET_FREQUENCY_BAND: int band = message.arg1;