diff options
103 files changed, 2474 insertions, 2528 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 7465ed92e469..ed08a70bfd8d 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -18,6 +18,7 @@ package android.accounts; import static android.Manifest.permission.GET_ACCOUNTS; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.Size; @@ -28,6 +29,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.res.Resources; import android.database.SQLException; import android.os.Build; @@ -265,6 +267,15 @@ public class AccountManager { "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; + /** + * Token for the special case where a UID has access only to an account + * but no authenticator specific auth tokens. + * + * @hide + */ + public static final String ACCOUNT_ACCESS_TOKEN = + "com.android.abbfd278-af8b-415d-af8b-7571d5dab133"; + private final Context mContext; private final IAccountManager mService; private final Handler mMainHandler; @@ -2960,4 +2971,49 @@ public class AccountManager { } }.start(); } + + /** + * Gets whether a given package under a user has access to an account. + * Can be called only from the system UID. + * + * @param account The account for which to check. + * @param packageName The package for which to check. + * @param userHandle The user for which to check. + * @return True if the package can access the account. + * + * @hide + */ + public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName, + @NonNull UserHandle userHandle) { + try { + return mService.hasAccountAccess(account, packageName, userHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Creates an intent to request access to a given account for a UID. + * The returned intent should be stated for a result where {@link + * Activity#RESULT_OK} result means access was granted whereas {@link + * Activity#RESULT_CANCELED} result means access wasn't granted. Can + * be called only from the system UID. + * + * @param account The account for which to request. + * @param packageName The package name which to request. + * @param userHandle The user for which to request. + * @return The intent to request account access or null if the package + * doesn't exist. + * + * @hide + */ + public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account, + @NonNull String packageName, @NonNull UserHandle userHandle) { + try { + return mService.createRequestAccountAccessIntentSenderAsUser(account, packageName, + userHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/accounts/AccountManagerInternal.java b/core/java/android/accounts/AccountManagerInternal.java new file mode 100644 index 000000000000..d777643950e6 --- /dev/null +++ b/core/java/android/accounts/AccountManagerInternal.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 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.accounts; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.RemoteCallback; + +/** + * Account manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class AccountManagerInternal { + + /** + * Requests that a given package is given access to an account. + * The provided callback will be invoked with a {@link android.os.Bundle} + * containing the result which will be a boolean value mapped to the + * {@link AccountManager#KEY_BOOLEAN_RESULT} key. + * + * @param account The account for which to request. + * @param packageName The package name for which to request. + * @param userId Concrete user id for which to request. + * @param callback A callback for receiving the result. + */ + public abstract void requestAccountAccess(@NonNull Account account, + @NonNull String packageName, @IntRange(from = 0) int userId, + @NonNull RemoteCallback callback); +} diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java index 12b2b9ccf148..8d0ce58d3358 100644 --- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java +++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java @@ -35,12 +35,10 @@ import java.io.IOException; */ public class GrantCredentialsPermissionActivity extends Activity implements View.OnClickListener { public static final String EXTRAS_ACCOUNT = "account"; - public static final String EXTRAS_AUTH_TOKEN_LABEL = "authTokenLabel"; public static final String EXTRAS_AUTH_TOKEN_TYPE = "authTokenType"; public static final String EXTRAS_RESPONSE = "response"; - public static final String EXTRAS_ACCOUNT_TYPE_LABEL = "accountTypeLabel"; - public static final String EXTRAS_PACKAGES = "application"; public static final String EXTRAS_REQUESTING_UID = "uid"; + private Account mAccount; private String mAuthTokenType; private int mUid; @@ -109,7 +107,11 @@ public class GrantCredentialsPermissionActivity extends Activity implements View } } }; - AccountManager.get(this).getAuthTokenLabel(mAccount.type, mAuthTokenType, callback, null); + + if (!AccountManager.ACCOUNT_ACCESS_TOKEN.equals(mAuthTokenType)) { + AccountManager.get(this).getAuthTokenLabel(mAccount.type, + mAuthTokenType, callback, null); + } findViewById(R.id.allow_button).setOnClickListener(this); findViewById(R.id.deny_button).setOnClickListener(this); diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 7199288426f2..56a6488088b5 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -19,8 +19,10 @@ package android.accounts; import android.accounts.IAccountManagerResponse; import android.accounts.Account; import android.accounts.AuthenticatorDescription; +import android.content.IntentSender; import android.os.Bundle; - +import android.os.RemoteCallback; +import android.os.UserHandle; /** * Central application service that provides account management. @@ -102,4 +104,10 @@ interface IAccountManager { /* Check if credentials update is suggested */ void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account, String statusToken); + + /* Check if the package in a user can access an account */ + boolean hasAccountAccess(in Account account, String packageName, in UserHandle userHandle); + /* Crate an intent to request account access for package and a given user id */ + IntentSender createRequestAccountAccessIntentSenderAsUser(in Account account, + String packageName, in UserHandle userHandle); } diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index ec22ff65a5f8..75a5bf7f77a2 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -44,7 +44,7 @@ interface IWallpaperManager { */ ParcelFileDescriptor setWallpaper(String name, in String callingPackage, in Rect cropHint, boolean allowBackup, out Bundle extras, int which, - IWallpaperManagerCallback completion); + IWallpaperManagerCallback completion, int userId); /** * Set the live wallpaper. This only affects the system wallpaper. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 6e4466238af2..219afea02c48 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -48,6 +48,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -939,7 +940,8 @@ public class WallpaperManager { /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), - mContext.getOpPackageName(), null, false, result, which, completion); + mContext.getOpPackageName(), null, false, result, which, completion, + UserHandle.myUserId()); if (fd != null) { FileOutputStream fos = null; boolean ok = false; @@ -1040,6 +1042,19 @@ public class WallpaperManager { public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { + return setBitmap(fullImage, visibleCropHint, allowBackup, which, + UserHandle.myUserId()); + } + + /** + * Like {@link #setBitmap(Bitmap, Rect, boolean, int)}, but allows to pass in an explicit user + * id. If the user id doesn't match the user id the process is running under, calling this + * requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * @hide + */ + public int setBitmap(Bitmap fullImage, Rect visibleCropHint, + boolean allowBackup, @SetWallpaperFlags int which, int userId) + throws IOException { validateRect(visibleCropHint); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); @@ -1050,7 +1065,7 @@ public class WallpaperManager { try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, - result, which, completion); + result, which, completion, userId); if (fd != null) { FileOutputStream fos = null; try { @@ -1176,7 +1191,7 @@ public class WallpaperManager { try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, - result, which, completion); + result, which, completion, UserHandle.myUserId()); if (fd != null) { FileOutputStream fos = null; try { diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java index 8a16ac94522e..6ef7fd214069 100644 --- a/core/java/android/content/SyncAdapterType.java +++ b/core/java/android/content/SyncAdapterType.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.Nullable; import android.text.TextUtils; import android.os.Parcelable; import android.os.Parcel; @@ -33,6 +34,7 @@ public class SyncAdapterType implements Parcelable { private final boolean isAlwaysSyncable; private final boolean allowParallelSyncs; private final String settingsActivity; + private final String packageName; public SyncAdapterType(String authority, String accountType, boolean userVisible, boolean supportsUploading) { @@ -50,6 +52,7 @@ public class SyncAdapterType implements Parcelable { this.allowParallelSyncs = false; this.settingsActivity = null; this.isKey = false; + this.packageName = null; } /** @hide */ @@ -57,7 +60,8 @@ public class SyncAdapterType implements Parcelable { boolean supportsUploading, boolean isAlwaysSyncable, boolean allowParallelSyncs, - String settingsActivity) { + String settingsActivity, + String packageName) { if (TextUtils.isEmpty(authority)) { throw new IllegalArgumentException("the authority must not be empty: " + authority); } @@ -72,6 +76,7 @@ public class SyncAdapterType implements Parcelable { this.allowParallelSyncs = allowParallelSyncs; this.settingsActivity = settingsActivity; this.isKey = false; + this.packageName = packageName; } private SyncAdapterType(String authority, String accountType) { @@ -89,6 +94,7 @@ public class SyncAdapterType implements Parcelable { this.allowParallelSyncs = false; this.settingsActivity = null; this.isKey = true; + this.packageName = null; } public boolean supportsUploading() { @@ -148,6 +154,16 @@ public class SyncAdapterType implements Parcelable { return settingsActivity; } + /** + * The package hosting the sync adapter. + * @return The package name. + * + * @hide + */ + public @Nullable String getPackageName() { + return packageName; + } + public static SyncAdapterType newKey(String authority, String accountType) { return new SyncAdapterType(authority, accountType); } @@ -181,6 +197,7 @@ public class SyncAdapterType implements Parcelable { + ", isAlwaysSyncable=" + isAlwaysSyncable + ", allowParallelSyncs=" + allowParallelSyncs + ", settingsActivity=" + settingsActivity + + ", packageName=" + packageName + "}"; } } @@ -201,6 +218,7 @@ public class SyncAdapterType implements Parcelable { dest.writeInt(isAlwaysSyncable ? 1 : 0); dest.writeInt(allowParallelSyncs ? 1 : 0); dest.writeString(settingsActivity); + dest.writeString(packageName); } public SyncAdapterType(Parcel source) { @@ -211,6 +229,7 @@ public class SyncAdapterType implements Parcelable { source.readInt() != 0, source.readInt() != 0, source.readInt() != 0, + source.readString(), source.readString()); } diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 6704b75dff7f..ddbdb8a7a559 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -81,7 +81,7 @@ public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> sa.getString(com.android.internal.R.styleable .SyncAdapter_settingsActivity); return new SyncAdapterType(authority, accountType, userVisible, supportsUploading, - isAlwaysSyncable, allowParallelSyncs, settingsActivity); + isAlwaysSyncable, allowParallelSyncs, settingsActivity, packageName); } finally { sa.recycle(); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 82cd448e7776..2093124d5b8a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2290,11 +2290,7 @@ public class PackageParser { b.append(cls); return b.toString().intern(); } - if (c >= 'a' && c <= 'z') { - return cls.intern(); - } - outError[0] = "Bad class name " + cls + " in package " + pkg; - return null; + return cls.intern(); } private static String buildCompoundName(String pkg, @@ -2755,12 +2751,7 @@ public class PackageParser { } if (ai.name != null) { - ai.className = buildClassName(pkgName, ai.name, outError); - if (ai.className == null) { - sa.recycle(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return false; - } + ai.className = ai.name; } String manageSpaceActivity = sa.getNonConfigurationString( diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 8d6d9ed567b6..4616af8f7768 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -372,6 +372,14 @@ public class Environment { } /** + * @see #getDataPreloadsDirectory() + * {@hide} + */ + public static File getDataPreloadsMediaDirectory() { + return new File(getDataPreloadsDirectory(), "media"); + } + + /** * Return the primary shared/external storage directory. This directory may * not currently be accessible if it has been mounted by the user on their * computer, has been removed from the device, or some other problem has diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 9383a8b1f4a9..e83614396aa9 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5839,6 +5839,8 @@ public final class Settings { /** * If nonzero, ANRs in invisible background processes bring up a dialog. * Otherwise, the process will be silently killed. + * + * Also prevents ANRs and crash dialogs from being suppressed. * @hide */ public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 872168a261e3..20c15a4953d7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -20280,8 +20280,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // remove it from the transparent region. final int[] location = attachInfo.mTransparentLocation; getLocationInWindow(location); - region.op(location[0], location[1], location[0] + mRight - mLeft, - location[1] + mBottom - mTop, Region.Op.DIFFERENCE); + // When a view has Z value, then it will be better to leave some area below the view + // for drawing shadow. The shadow outset is proportional to the Z value. Note that + // the bottom part needs more offset than the left, top and right parts due to the + // spot light effects. + int shadowOffset = getZ() > 0 ? (int) getZ() : 0; + region.op(location[0] - shadowOffset, location[1] - shadowOffset, + location[0] + mRight - mLeft + shadowOffset, + location[1] + mBottom - mTop + (shadowOffset * 3), Region.Op.DIFFERENCE); } else { if (mBackground != null && mBackground.getOpacity() != PixelFormat.TRANSPARENT) { // The SKIP_DRAW flag IS set and the background drawable exists, we remove diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 3ff8d4f2e2b1..6933efc15b68 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -6406,16 +6406,28 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return true; } super.gatherTransparentRegion(region); - final View[] children = mChildren; - final int count = mChildrenCount; + // Instead of naively traversing the view tree, we have to traverse according to the Z + // order here. We need to go with the same order as dispatchDraw(). + // One example is that after surfaceView punch a hole, we will still allow other views drawn + // on top of that hole. In this case, those other views should be able to cut the + // transparent region into smaller area. + final int childrenCount = mChildrenCount; boolean noneOfTheChildrenAreTransparent = true; - for (int i = 0; i < count; i++) { - final View child = children[i]; - if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { - if (!child.gatherTransparentRegion(region)) { - noneOfTheChildrenAreTransparent = false; + if (childrenCount > 0) { + final ArrayList<View> preorderedList = buildOrderedChildList(); + final boolean customOrder = preorderedList == null + && isChildrenDrawingOrderEnabled(); + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { + if (!child.gatherTransparentRegion(region)) { + noneOfTheChildrenAreTransparent = false; + } } } + if (preorderedList != null) preorderedList.clear(); } return meOpaque || noneOfTheChildrenAreTransparent; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4dc1009fe446..7494b94ce713 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -52,6 +52,7 @@ import android.hardware.input.InputManager; import android.media.AudioManager; import android.os.Binder; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -1540,7 +1541,15 @@ public final class ViewRootImpl implements ViewParent, if (viewVisibilityChanged) { mAttachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); - host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); + + // Prior to N we didn't have dispatchVisibilityAggregated to give a more accurate + // view into when views are visible to the user or not. ImageView never dealt with + // telling its drawable about window visibility, among other things. Some apps cause + // an additional crossfade animation when windows become visible if they get this + // additional call, so only send it to new apps to avoid new visual jank. + if (host.getContext().getApplicationInfo().targetSdkVersion >= VERSION_CODES.N) { + host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); + } if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { endDragResizing(); destroyHardwareResources(); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 6432f703f2ba..5935c7889e9c 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1547,7 +1547,7 @@ public class PopupWindow { } // Let the window manager know to align the top to y. - outParams.gravity = Gravity.LEFT | Gravity.TOP; + outParams.gravity = computeGravity(); outParams.width = width; outParams.height = height; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3339d02d9bbe..8bdb55ec0b63 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1313,6 +1313,7 @@ android:protectionLevel="dangerous" android:description="@string/permdesc_getAccounts" android:label="@string/permlab_getAccounts" /> + <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <!-- @SystemApi Allows applications to call into AccountAuthenticators. <p>Not for use by third-party applications. --> diff --git a/core/res/res/values-mcc222-mnc10/config.xml b/core/res/res/values-mcc222-mnc10/config.xml index cd6e8c6f46c2..c819de2ba43c 100644 --- a/core/res/res/values-mcc222-mnc10/config.xml +++ b/core/res/res/values-mcc222-mnc10/config.xml @@ -28,29 +28,4 @@ <string-array translatable="false" name="config_tether_apndata"> <item>Tethering Internet,web.omnitel.it,,,,,,,,,222,10,,DUN</item> </string-array> - - <string-array translatable="false" name="config_operatorConsideredNonRoaming"> - <item>21401</item> - <item>21402</item> - <item>21403</item> - <item>21404</item> - <item>21405</item> - <item>21406</item> - <item>21407</item> - <item>21408</item> - <item>21409</item> - <item>21410</item> - <item>21411</item> - <item>21412</item> - <item>21413</item> - <item>21414</item> - <item>21415</item> - <item>21416</item> - <item>21417</item> - <item>21418</item> - <item>21419</item> - <item>21420</item> - <item>21421</item> - </string-array> - </resources> diff --git a/core/res/res/values-mcc232-mnc10/config.xml b/core/res/res/values-mcc232-mnc10/config.xml new file mode 100644 index 000000000000..bdf83016d18e --- /dev/null +++ b/core/res/res/values-mcc232-mnc10/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + ** Copyright 2016, 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. + */ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>23203</item> + <item>23205</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc232-mnc13/config.xml b/core/res/res/values-mcc232-mnc13/config.xml new file mode 100644 index 000000000000..2c14f87374f6 --- /dev/null +++ b/core/res/res/values-mcc232-mnc13/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + ** Copyright 2016, 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. + */ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>23203</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc302-mnc500/config.xml b/core/res/res/values-mcc302-mnc500/config.xml new file mode 100644 index 000000000000..77f64199a80b --- /dev/null +++ b/core/res/res/values-mcc302-mnc500/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + ** Copyright 2016, 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. + */ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>302</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc302-mnc510/config.xml b/core/res/res/values-mcc302-mnc510/config.xml new file mode 100644 index 000000000000..77f64199a80b --- /dev/null +++ b/core/res/res/values-mcc302-mnc510/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + ** Copyright 2016, 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. + */ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>302</item> + </string-array> +</resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6c26165d4a3a..fbd57df87ebd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2929,6 +2929,18 @@ <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. --> <string name="wifi_no_internet_detailed">Tap for options</string> + <!-- Network type names used in the network_switch_metered and network_switch_metered_detail strings. These must be kept in the sync with the values NetworkCapabilities.TRANSPORT_xxx values, and in the same order. --> + <string-array name="network_switch_type_name"> + <item>cellular data</item> + <item>Wi-Fi</item> + <item>Bluetooth</item> + <item>Ethernet</item> + <item>VPN</item> + </string-array> + + <!-- Network type name displayed if one of the types is not found in network_switch_type_name. --> + <string name="network_switch_type_name_unknown">an unknown network type</string> + <!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. This is the notification's title / ticker. --> <string name="wifi_watchdog_network_disabled">Couldn\'t connect to Wi-Fi</string> <!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. The complete alert msg is: <hotspot name> + this string, i.e. "Linksys has a poor internet connection" --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b31605d21ecd..11e83d87d10a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -947,6 +947,8 @@ <java-symbol type="string" name="wifi_available_sign_in" /> <java-symbol type="string" name="network_available_sign_in" /> <java-symbol type="string" name="network_available_sign_in_detailed" /> + <java-symbol type="array" name="network_switch_type_name" /> + <java-symbol type="string" name="network_switch_type_name_unknown" /> <java-symbol type="string" name="wifi_no_internet" /> <java-symbol type="string" name="wifi_no_internet_detailed" /> <java-symbol type="string" name="wifi_connect_alert_title" /> diff --git a/docs/html-intl/intl/es/about/versions/nougat/android-7.0-testing.jd b/docs/html-intl/intl/es/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index 20d2d6e686dd..000000000000 --- a/docs/html-intl/intl/es/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=Guía de prueba -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="tb-wrapper"> - <div id="tb"> - <h2>En este documento</h2> - <ol> - <li><a href="#runtime-permissions">Prueba de los permisos</a></li> - <li><a href="#doze-standby">Prueba de los modos Descanso y App Standby</a></li> - <li><a href="#ids">Copia de seguridad automática e identificadores de dispositivos</a></li> - </ol> - </div> -</div> - -<p> - Android N te brinda la oportunidad de garantizar que tus aplicaciones funcionen con la próxima versión de la plataforma. - Esta versión preliminar incluye diversas API y cambios en los comportamientos que pueden -tener impactos en tu aplicación, como se describe en las secciones <a href="{@docRoot}preview/api-overview.html">Información general de la API</a> y <a href="{@docRoot}preview/behavior-changes.html">Cambios en los comportamientos</a>. - Al probar tu aplicación con la versión preliminar, te debes centrar en algunos cambios específicos del sistema para garantizar que los usuarios disfruten de una buena experiencia. - - -</p> - -<p> - En esta guía, se describen qué y cómo probar las características preliminares con tu aplicación. Debes priorizar la prueba de estas características específicas preliminares, puesto que podrían tener un alto impacto en el comportamiento de tu aplicación: - - -</p> - -<ul> - <li><a href="#runtime-permissions">Permisos</a> - </li> - <li><a href="#doze-standby">Modos Descanso y App Standby</a> - </li> - <li><a href="#ids">Copia de seguridad automática e identificadores de dispositivos</a></li> -</ul> - -<p> - Para obtener más información sobre cómo configurar dispositivos o dispositivos virtuales con una imagen - del sistema de la versión preliminar para realizar pruebas, consulta la sección <a href="{@docRoot}preview/setup-sdk.html">Configurar el SDK de Android N</a>. - -</p> - - -<h2 id="runtime-permissions">Prueba de los permisos</h2> - -<p> - El nuevo modelo de <a href="{@docRoot}preview/features/runtime-permissions.html">permisos</a> cambia el modo en que el usuario asigna permisos a tu aplicación. - En lugar de conceder todos los permisos durante el procedimiento de instalación, tu aplicación debe solicitar al usuario los permisos individuales en el tiempo de ejecución. - - Para los usuarios, este comportamiento ofrece más control granular sobre las actividades de cada aplicación, así como un mejor contexto para comprender por qué la aplicación está solicitando un permiso específico. - Los usuarios pueden conceder o revocar los permisos concedidos a una aplicación de forma individual en cualquier momento. - Es muy probable que esta característica de la versión preliminar tenga un impacto en el comportamiento de tu aplicación y puede hacer que algunas características de tu aplicación no funcionen o funcionen en un estado degradado. - - -</p> - -<p class="caution"> - Este cambio afecta a todas las aplicaciones que se ejecutan en la nueva plataforma, incluso a aquellas que no tienen como destino la nueva versión de la plataforma. - La plataforma ofrece un comportamiento de compatibilidad limitada para las aplicaciones heredadas, pero debes comenzar a planificar ahora la migración de tu aplicación al nuevo modelo de permisos, con el objetivo de publicar una versión actualizada de tu aplicación cuando se lance la plataforma oficial. - - -</p> - - -<h3 id="permission-test-tips">Tips para pruebas</h3> - -<p> - Usa los siguientes tips para pruebas como ayuda para planificar y ejecutar las pruebas de tu aplicación con el nuevo comportamiento de permisos. - -</p> - -<ul> - <li>Identifica los permisos actuales de tu aplicación y las rutas de códigos relacionadas.</li> - <li>Prueba los flujos del usuario en los datos y servicios protegidos por permisos.</li> - <li>Realiza pruebas con varias combinaciones de permisos concedidos/revocados.</li> - <li>Usa la herramienta {@code adb} para administrar permisos desde la línea de comando: - <ul> - <li>Enumera los permisos y estados por grupo: - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>Concede o revoca un permiso o más permisos utilizando la siguiente sintaxis:<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>Analiza tu aplicación para detectar servicios que utilizan permisos.</li> -</ul> - -<h3 id="permission-test-strategy">Estrategia de prueba</h3> - -<p> - El cambio en los permisos afecta la estructura y el diseño de tu aplicación, además de la experiencia del usuario y los flujos que proporcionas a los usuarios. - Debes evaluar el uso de los permisos actuales de tu aplicación y comenzar a planificar los nuevos flujos que deseas ofrecer. - La versión oficial de la plataforma proporciona un comportamiento de compatibilidad, pero debes prever la actualización de tu aplicación y no depender de estos comportamientos. - - -</p> - -<p> - Identifica los permisos que tu aplicación verdaderamente necesita y utiliza, y luego busca las diversas rutas de códigos que utilizan los servicios protegidos por permisos. - Puedes realizar esto mediante una combinación de pruebas en la plataforma nueva y análisis de códigos. - Al realizar las pruebas, debes centrarte en - incluir permisos de tiempo de ejecución cambiando {@code targetSdkVersion} de la aplicación a la versión preliminar. Para - obtener más información, consulta la sección <a href="{@docRoot}preview/setup-sdk.html#">Configurar el SDK de Android N</a>. - -</p> - -<p> - Realiza pruebas con diversas combinaciones de permisos revocados y agregados, a fin de destacar los flujos del usuario que dependen de permisos. - Cuando una dependencia no sea obvia ni lógica, debes considerar la opción de refactorizar o compartimentar ese flujo para eliminar la dependencia o aclarar por qué se necesita el permiso. - - -</p> - -<p> - Para obtener más información sobre el comportamiento de los permisos de tiempo de ejecución, las pruebas y las mejores prácticas, consulta la página <a href="{@docRoot}preview/features/runtime-permissions.html">Permisos</a> de la versión preliminar para desarrolladores. - - -</p> - - -<h2 id="doze-standby">Prueba de los modos Descanso y App Standby</h2> - -<p> - Las características de ahorro de energía de los modos Descanso y App Standby limitan la cantidad de procesamiento en segundo plano que puede realizar tu aplicación cuando un dispositivo se encuentra en estado inactivo o mientras tu aplicación no está en foco. - Entre las restricciones que el sistema puede imponer en las aplicaciones se incluyen el acceso limitado a la red o denegación de acceso, suspensión de las tareas en segundo plano, suspensión de notificaciones, y alarmas y solicitudes de reactivación ignoradas. - - Para garantizar que tu aplicación tenga un comportamiento correcto con estas optimizaciones de ahorro de energía, debes probar tu aplicación simulando estos estados de bajo consumo. - - -</p> - -<h4 id="doze">Cómo probar la aplicación en modo Descanso</h4> - -<p>Para probar el modo Descanso con tu aplicación, realiza lo siguiente:</p> - -<ol> -<li>Configura un dispositivo de hardware o un dispositivo virtual con una imagen del sistema Android N.</li> -<li>Conecta el dispositivo a tu equipo de desarrollo e instala tu aplicación.</li> -<li>Ejecuta tu aplicación y déjala activa.</li> -<li>Simula la activación del modo Descanso en el dispositivo ejecutando los siguientes comandos: - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>Observa el comportamiento de tu aplicación cuando se reactive el dispositivo. Asegúrate de que se recupere correctamente cuando el dispositivo salga del modo Descanso. -</li> -</ol> - - -<h4 id="standby">Cómo probar aplicaciones en modo App Standby</h4> - -<p>Para probar el modo App Standby con tu aplicación, realiza lo siguiente:</p> - -<ol> - <li>Configura un dispositivo de hardware o un dispositivo virtual con una imagen del sistema Android N.</li> - <li>Conecta el dispositivo a tu equipo de desarrollo e instala tu aplicación.</li> - <li>Ejecuta tu aplicación y déjala activa.</li> - <li>Simula la activación del modo App Standby en la aplicación ejecutando los siguientes comandos: - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>Simula la activación de tu aplicación con el siguiente comando: - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>Observa el comportamiento de tu aplicación al reactivarse. Asegúrate de que se recupere correctamente del modo App Standby. - En particular, debes comprobar si los trabajos en segundo plano y las notificaciones de tu aplicación continúan funcionando de la manera esperada. -</li> -</ol> - -<h2 id="ids">Copia de seguridad automática para aplicaciones e identificadores específicos del dispositivo</h2> - -<p>Si tu aplicación continúa teniendo algún identificador específico del dispositivo, como la Id. de registro de Google Cloud Messaging, en el almacenamiento interno, asegúrate de seguir las mejores prácticas para excluir la ubicación de almacenamiento de la copia de seguridad automática, como se describe en la sección <a href="{@docRoot}preview/backup/index.html">Copia de seguridad automática para aplicaciones</a>. - - - - </p> diff --git a/docs/html-intl/intl/es/about/versions/nougat/index.jd b/docs/html-intl/intl/es/about/versions/nougat/index.jd index c9312708ce6f..b30cc88e0644 100644 --- a/docs/html-intl/intl/es/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/es/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,62 +17,56 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - ¡Prepárate para Android N! + ¡Prepárate para Android Nougat! <strong>Prueba tus aplicaciones</strong> en Nexus y en otros dispositivos. Admite comportamientos del sistema nuevo para <strong>ahorrar energía y memoria</strong>. Amplía la funcionalidad de tus aplicaciones gracias a una <strong>IU con ventanas múltiples</strong>, <strong>notificaciones de respuestas directas</strong> y más. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> Comencemos - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Informa un problema </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - Consulta las notas de la versión - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Únete a la comunidad de desarrolladores @@ -113,19 +107,33 @@ footer.hide=1 data-initial-results="3"></div> </div></section> -<section class="dac-section dac-gray"><div class="wrap"> +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">Recursos</h1> <div class="dac-section-subtitle"> - Información esencial para ayudarte a preparar tus aplicaciones para Android N. + Información esencial para ayudarte a preparar tus aplicaciones para Android Nougat. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/es/index.jd b/docs/html-intl/intl/es/index.jd index 66f9bf0281d0..e0d80c1a7843 100644 --- a/docs/html-intl/intl/es/index.jd +++ b/docs/html-intl/intl/es/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html-intl/intl/in/about/versions/nougat/android-7.0-testing.jd b/docs/html-intl/intl/in/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index 94bc74cf3ca3..000000000000 --- a/docs/html-intl/intl/in/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=Panduan Pengujian -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="tb-wrapper"> - <div id="tb"> - <h2>Dalam dokumen ini</h2> - <ol> - <li><a href="#runtime-permissions">Izin Pengujian</a></li> - <li><a href="#doze-standby">Menguji Istirahatkan dan Aplikasi Siaga</a></li> - <li><a href="#ids">Pencadangan Otomatis dan Identifier Perangkat</a></li> - </ol> - </div> -</div> - -<p> - Android N memberi Anda kesempatan untuk memastikan aplikasi bekerja pada - platform versi berikutnya. Pratinjau ini berisi beberapa API dan perubahan perilaku yang bisa - memengaruhi aplikasi Anda, sebagaimana dijelaskan dalam <a href="{@docRoot}preview/api-overview.html">Ringkasan - API</a> dan <a href="{@docRoot}preview/behavior-changes.html">Perubahan Perilaku</a>. Dalam menguji - aplikasi dengan pratinjau, ada beberapa perubahan sistem spesifik yang harus Anda fokuskan untuk - memastikan pengguna mendapatkan pengalaman yang bagus. -</p> - -<p> - Panduan ini menjelaskan apa dan bagaimana menguji fitur pratinjau dengan aplikasi Anda. Anda harus - mengutamakan pengujian fitur pratinjau spesifik ini, dikarenakan pengaruhnya yang besar pada - perilaku aplikasi Anda: -</p> - -<ul> - <li><a href="#runtime-permissions">Izin</a> - </li> - <li><a href="#doze-standby">Istirahatkan dan Aplikasi Siaga</a> - </li> - <li><a href="#ids">Pencadangan Otomatis dan Identifier Perangkat</a></li> -</ul> - -<p> - Untuk informasi selengkapnya tentang cara menyiapkan perangkat atau perangkat maya dengan citra sistem pratinjau - untuk pengujian, lihat <a href="{@docRoot}preview/setup-sdk.html">Menyiapkan -Android N SDK</a>. -</p> - - -<h2 id="runtime-permissions">Izin Pengujian</h2> - -<p> - Model <a href="{@docRoot}preview/features/runtime-permissions.html">Izin</a> yang baru - mengubah cara alokasi izin untuk aplikasi Anda oleh pengguna. Sebagai ganti memberi semua - izin selama prosedur pemasangan, aplikasi Anda harus meminta izin kepada pengguna secara individual - pada waktu proses. Bagi pengguna, perilaku ini memberi kontrol yang lebih detail atas setiap aktivitas aplikasi, dan - juga konteks yang lebih untuk memahami sebab aplikasi meminta izin tertentu. Pengguna - bisa memberi atau mencabut izin yang diberikan pada suatu aplikasi secara individual kapan saja. Fitur - pratinjau ini kemungkinan besar memengaruhi perilaku aplikasi Anda dan mungkin menghambat fungsi beberapa - fitur aplikasi Anda, atau mengurangi kualitas kerjanya. -</p> - -<p class="caution"> - Perubahan ini memengaruhi semua aplikasi yang berjalan di platform baru, bahkan aplikasi yang tidak menargetkan versi - platform baru. Platform ini memberikan perilaku kompatibilitas terbatas untuk aplikasi lawas, namun Anda - harus mulai merencanakan migrasi aplikasi ke model izin baru sekarang juga, dengan tujuan - mempublikasikan versi terbaru aplikasi Anda saat peluncuran platform secara resmi. -</p> - - -<h3 id="permission-test-tips">Tip pengujian</h3> - -<p> - Gunakan tip berikut untuk membantu Anda merencanakan dan menjalankan pengujian aplikasi dengan - perilaku izin yang baru. -</p> - -<ul> - <li>Identifikasi izin aplikasi Anda saat ini dan jalur kode terkait.</li> - <li>Uji alur pengguna pada semua layanan dan data yang dilindungi izin.</li> - <li>Uji dengan berbagai kombinasi izin yang diberikan/dicabut.</li> - <li>Gunakan alat bantu {@code adb} untuk mengelola izin dari baris perintah: - <ul> - <li>Cantumkan daftar izin dan status berdasarkan kelompok: - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>Beri atau cabut satu atau beberapa izin menggunakan sintaks berikut:<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>Analisis aplikasi Anda untuk layanan yang menggunakan izin.</li> -</ul> - -<h3 id="permission-test-strategy">Strategi pengujian</h3> - -<p> - Perubahan izin memengaruhi struktur dan desain aplikasi Anda, begitu juga - pengalaman pengguna dan alur yang Anda sediakan untuk pengguna. Anda harus menilai penggunaan izin - aplikasi saat ini dan mulai merencanakan alur baru yang ingin ditawarkan. Rilis platform - resmi menyediakan perilaku kompatibilitas, namun Anda harus merencanakan pembaruan aplikasi dan tidak - bergantung pada perilaku ini. -</p> - -<p> - Identifikasi izin yang sebenarnya diperlukan dan digunakan aplikasi Anda, kemudian temukan berbagai - jalur kode yang menggunakan layanan yang dilindungi izin. Anda bisa melakukan ini melalui kombinasi - pengujian pada platform baru dan analisis kode. Dalam pengujian, Anda harus fokus pada pemilihan - izin waktu proses dengan mengubah {@code targetSdkVersion} aplikasi ke versi pratinjau. Untuk - informasi selengkapnya, lihat <a href="{@docRoot}preview/setup-sdk.html#">Menyiapkan -Android N SDK</a>. -</p> - -<p> - Uji dengan berbagai kombinasi izin yang dicabut dan ditambahkan, untuk menyoroti alur pengguna yang - bergantung pada izin. Jika dependensi tidak jelas atau logis, Anda harus mempertimbangkan -optimalisasi atau kompartementalisasi alur tersebut untuk mengeliminasi dependensi atau menjelaskan alasan - diperlukannya izin. -</p> - -<p> - Untuk informasi selengkapnya tentang perilaku izin waktu proses, pengujian, dan praktik terbaik, lihat - halaman pratinjau <a href="{@docRoot}preview/features/runtime-permissions.html">Izin</a> - pengembang. -</p> - - -<h2 id="doze-standby">Menguji Istirahatkan dan Aplikasi Siaga</h2> - -<p> - Fitur penghematan daya Istirahatkan dan Aplikasi Siaga membatasi jumlah pemrosesan latar belakang yang - bisa dikerjakan aplikasi Anda saat perangkat dalam keadaan diam atau saat aplikasi Anda sedang tidak fokus. Pembatasan - yang dapat diberlakukan oleh sistem pada aplikasi termasuk akses jaringan terbatas atau tidak ada, - tugas latar belakang yang ditangguhkan, Pemberitahuan yang ditangguhkan, permintaan membangunkan yang diabaikan, serta alarm. Untuk memastikan - aplikasi Anda berperilaku dengan benar pada optimalisasi penghematan daya ini, Anda harus menguji aplikasi dengan - menyimulasikan keadaan baterai yang sedang tinggal sedikit ini. -</p> - -<h4 id="doze">Menguji aplikasi Anda dengan Istirahatkan</h4> - -<p>Untuk menguji Istirahatkan dengan aplikasi Anda:</p> - -<ol> -<li>Konfigurasikan perangkat keras atau perangkat maya dengan citra sistem Android N.</li> -<li>Hubungkan perangkat dengan mesin pengembangan dan pasang aplikasi Anda.</li> -<li>Jalankan aplikasi Anda dan biarkan aktif.</li> -<li>Simulasikan perangkat yang sedang masuk ke dalam mode Istirahatkan dengan menjalankan perintah berikut: - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>Amati perilaku aplikasi Anda saat perangkat diaktifkan kembali. Pastikan aplikasi - pulih dengan baik saat perangkat keluar dari Istirahatkan.</li> -</ol> - - -<h4 id="standby">Menguji aplikasi dengan Aplikasi Siaga</h4> - -<p>Untuk menguji mode Aplikasi Siaga dengan aplikasi Anda:</p> - -<ol> - <li>Konfigurasikan perangkat keras atau perangkat maya dengan citra sistem Android N.</li> - <li>Hubungkan perangkat dengan mesin pengembangan dan pasang aplikasi Anda.</li> - <li>Jalankan aplikasi Anda dan biarkan aktif.</li> - <li>Simulasikan aplikasi yang sedang masuk ke dalam mode siaga dengan menjalankan perintah berikut: - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>Simulasikan membangunkan aplikasi Anda menggunakan perintah berikut: - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>Amati perilaku aplikasi Anda saat dibangunkan. Pastikan aplikasi pulih dengan baik - dari mode siaga. Secara khusus, Anda harus memeriksa apakah Pemberitahuan aplikasi dan pekerjaan latar belakang - tetap berjalan sebagaimana yang diharapkan.</li> -</ol> - -<h2 id="ids">Auto Backup for Apps dan Identifier Perangkat Spesifik</h2> - -<p>Jika aplikasi Anda mempertahankan identifier perangkat spesifik, seperti ID pendaftaran Google -Cloud Messaging, dalam penyimpanan internal, -pastikan Anda mengikuti praktik terbaik untuk mengecualikan lokasi -penyimpanan dari pencadangan otomatis, seperti dijelaskan dalam <a href="{@docRoot}preview/backup/index.html">Auto -Backup for Apps</a>. </p> diff --git a/docs/html-intl/intl/in/about/versions/nougat/index.jd b/docs/html-intl/intl/in/about/versions/nougat/index.jd index a8f61ebb547b..5234f91d08e7 100644 --- a/docs/html-intl/intl/in/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/in/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,66 +17,61 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - Bersiaplah menyambut Android N! + Bersiaplah menyambut Android Nougat! <strong>Uji aplikasi Anda</strong> pada perangkat Nexus dan perangkat lainnya. Dukung perilaku sistem baru untuk <strong>menghemat daya dan memori</strong>. Tambah aplikasi Anda dengan <strong>UI multi-jendela</strong>, <strong>pemberitahuan balasan langsung</strong> dan lainnya. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> Mulai - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> + <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button="" href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Laporkan masalah - </a> - </li> - <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - Lihat catatan rilis - </a> + </a> </li> <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Bergabunglah dengan komunitas pengembang - </a> + </a> </li> </ul> </div><!-- end .wrap --> @@ -113,19 +108,33 @@ footer.hide=1 data-initial-results="3"></div> </div></section> -<section class="dac-section dac-gray"><div class="wrap"> +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">Sumber Daya</h1> <div class="dac-section-subtitle"> - Informasi penting guna membantu mempersiapkan aplikasi untuk Android N. + Informasi penting guna membantu mempersiapkan aplikasi untuk Android Nougat. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/ja/about/versions/nougat/android-7.0-testing.jd b/docs/html-intl/intl/ja/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index 9f034120c776..000000000000 --- a/docs/html-intl/intl/ja/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=テストガイド -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="tb-wrapper"> - <div id="tb"> - <h2>このドキュメントの内容</h2> - <ol> - <li><a href="#runtime-permissions">パーミッションをテストする</a></li> - <li><a href="#doze-standby">Doze とアプリ スタンバイをテストする</a></li> - <li><a href="#ids">自動バックアップと端末識別子</a></li> - </ol> - </div> -</div> - -<p> - Android N を利用すると、次期バージョンのプラットフォームでアプリが動作するか確認できます。 -このプレビューには、<a href="{@docRoot}preview/api-overview.html">API の概要</a>と<a href="{@docRoot}preview/behavior-changes.html">動作の変更点</a>に記載されているように、アプリに影響を与える可能性のある多くの API と動作の変更が含まれています。 - -このプレビューでアプリをテストするときには、アプリの良好な使用感を確保するために、システムのいくつかの変更点に特に注意する必要があります。 - - -</p> - -<p> - このガイドでは、アプリでプレビューの機能の何をどのようにテストすればよいか説明します。以下のプレビュー機能は、アプリの動作に大きな影響を与える可能性があるので、優先してテストする必要があります。 - - -</p> - -<ul> - <li><a href="#runtime-permissions">パーミッション</a> - </li> - <li><a href="#doze-standby">Doze とアプリ スタンバイ</a> - </li> - <li><a href="#ids">自動バックアップと端末識別子</a></li> -</ul> - -<p> - テスト用のプレビュー システム イメージを使用した端末または仮想端末のセットアップ方法の詳細については、<a href="{@docRoot}preview/setup-sdk.html">Android N SDK のセットアップ</a>をご覧ください。 - - -</p> - - -<h2 id="runtime-permissions">パーミッションをテストする</h2> - -<p> - <a href="{@docRoot}preview/features/runtime-permissions.html">パーミッション</a> モデルの変更により、ユーザーがアプリにパーミッションを付与する方法が変わりました。 -アプリでは、インストール時にすべてのパーミッションを要求するのではなく、実行時に個々のパーミッションをユーザーに要求する必要があります。 - -これにより、ユーザーは、各アプリのアクティビティをより細かくコントロールできるようになるだけではなく、アプリが各パーミッションを要求する理由をこれまでよりもよく理解できるようになります。 -ユーザーは、いつでもアプリに個別にパーミッションを付与したり、付与したパーミッションを個別に取り消したりできます。 -プレビューのこの機能は、アプリの動作に大きな影響を与える可能性があり、アプリの一部の機能が動作しなくなったり、限定された機能しか使えなくなったりする可能性もあります。 - - -</p> - -<p class="caution"> - この変更は、アプリがこの新しいバージョンを対象にしているかどうかにかかわらず、この新しいプラットフォーム上で実行されるすべてのアプリに影響します。 -このプラットフォームは以前のアプリに限定的な互換動作を提供しますが、公式版のプラットフォームのリリースに合わせてアップデート版のアプリを公開できるように、新しいパーミッション モデルに対応させるためのアプリの移行を今から計画することを強くお勧めします。 - - -</p> - - -<h3 id="permission-test-tips">テストのヒント</h3> - -<p> - 以下のテストのヒントを活用して、アプリでの新しいパーミッション動作のテストを計画し、実行してください。 - -</p> - -<ul> - <li>アプリの現在のパーミッションと関連するコードパスを確認します。</li> - <li>パーミッションで保護されているサービスとデータ間のユーザーフローをテストします。</li> - <li>付与されたパーミッションと取り消されたパーミッションのさまざまな組み合わせをテストします。</li> - <li>{@code adb} ツールを使用して、コマンドラインからパーミッションを管理します。 - <ul> - <li>パーミッションとステータスをグループ化して表示します。 - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>以下の構文を使用して 1 つまたは複数のパーミッションを付与または取り消します。<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>アプリでパーミッションを使用しているサービスを分析します。</li> -</ul> - -<h3 id="permission-test-strategy">テスト方針</h3> - -<p> - このパーミッションの変化は、アプリの構造と設計、ユーザー エクスペリエンスとフローに影響を与えます。 -アプリの現在のパーミッション利用の状況を調査し、新しいフローの検討を開始する必要があります。 -このプラットフォームの公式リリースは互換動作を提供しますが、互換動作に頼ることなくアプリのアップデートを計画することを強くお勧めします。 - - -</p> - -<p> - まずアプリが実際に必要とし使用しているパーミッションを特定してから、パーミッションで保護されたサービスを使用している各コードパスを探してください。 -これには、新しいプラットフォーム上でのテストと、コードの解析が必要です。 -テストでは、アプリの {@code targetSdkVersion} をこのプレビュー版に変えて、ランタイム パーミッションのオプトインに重点的にテストする必要があります。 -詳細については、<a href="{@docRoot}preview/setup-sdk.html#">Android N SDK のセットアップ</a>をご覧ください。 - - -</p> - -<p> - パーミッションの取り消しと追加のさまざまな組み合わせをテストし、パーミッションに依存するユーザーフローを確認します。 -パーミッションへの依存性が明白または論理的ではない箇所では、依存性を取り除くため、またはパーミッションが必要な理由を明白にするために、フローのリファクタリングまたはコンパートメント化を検討する必要があります。 - - -</p> - -<p> - ランタイム パーミッションの動作、テスト、ベスト プラクティスについては、Developer Preview ページの<a href="{@docRoot}preview/features/runtime-permissions.html">パーミッション</a>をご覧ください。 - - -</p> - - -<h2 id="doze-standby">Doze とアプリ スタンバイをテストする</h2> - -<p> - 省電力機能である Doze とアプリ スタンバイにより、端末がアイドル状態のときやそのアプリにフォーカスがないときに、アプリが実行できるバックグラウンド処理の量が制限されます。 -システムによってアプリに加えられる可能性のある制限には、ネットワーク アクセスの制限や停止、バックグラウンド タスクの停止、通知の停止、ウェイク リクエストの無視、アラームなどがあります。 - -これらの省電力のための最適化が行われた状態で確実にアプリが適切に動作するように、これらの省電力状態をシミュレートしてアプリをテストする必要があります。 - - -</p> - -<h4 id="doze">アプリで Doze をテストする</h4> - -<p>アプリで Doze をテストするには: </p> - -<ol> -<li>Android N のシステム イメージを使用して、ハードウェア端末または仮想端末を設定します。</li> -<li>端末を開発マシンに接続し、アプリをインストールします。</li> -<li>アプリを実行し、アクティブ状態のままにします。</li> -<li>以下のコマンドを実行して、端末の Doze モードへの移行をシミュレートします。 - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>端末がアクティブ状態に戻ったときのアプリの動作を観察します。端末が Doze モードから抜けるときに、アプリがスムーズに復帰することを確認します。 -</li> -</ol> - - -<h4 id="standby">アプリでアプリ スタンバイをテストする</h4> - -<p>アプリでアプリ スタンバイ モードをテストするには: </p> - -<ol> - <li>Android N のシステム イメージを使用して、ハードウェア端末または仮想端末を設定します。</li> - <li>端末を開発マシンに接続し、アプリをインストールします。</li> - <li>アプリを実行し、アクティブ状態のままにします。</li> - <li>以下のコマンドを実行して、アプリのスタンバイ モードへの移行をシミュレートします。 - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>以下のコマンドを使用して、アプリのウェイクをシミュレートします。 - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>アプリがウェイク状態に戻ったときのアプリの動作を観察します。アプリがスタンバイ モードからスムーズに復帰することを確認します。 -特に、アプリの通知とバックグラウンド ジョブが想定通りの動作を続けているかを確認する必要があります。 -</li> -</ol> - -<h2 id="ids">アプリの自動バックアップと端末固有識別子</h2> - -<p>アプリが、Google Cloud Messaging の登録 ID などのなんらかの端末固有の識別子を内部ストレージに保持している場合、<a href="{@docRoot}preview/backup/index.html">アプリの自動バックアップ</a>の説明に従って、そのストレージのロケーションを自動バックアップの対象から除外してください。 - - - - </p> diff --git a/docs/html-intl/intl/ja/about/versions/nougat/index.jd b/docs/html-intl/intl/ja/about/versions/nougat/index.jd index 774e065634f3..5881cf60f945 100644 --- a/docs/html-intl/intl/ja/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/ja/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,66 +17,61 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - Android N が正式リリースされる前に、 + Android Nougat が正式リリースされる前に、 Nexus や他の端末で事前に<strong>アプリの動作をご確認いただけます</strong>。新しいシステム動作をサポートして、<strong>電力やメモリの使用量を削減しましょう</strong>。 <strong>マルチ ウィンドウ UI</strong> や<strong>ダイレクト リプライ通知</strong>などの機能も利用して、アプリを拡張してみてください。 </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> - スタートガイド</a> -<!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + スタートガイド + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> + <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - 問題の報告</a> - - </li> - <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - リリースノートの確認</a> - + 問題の報告 + </a> </li> <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - 開発者コミュニティに参加</a> - + 開発者コミュニティに参加 + </a> </li> </ul> </div><!-- end .wrap --> @@ -113,19 +108,33 @@ footer.hide=1 data-initial-results="3"></div> </div></section> -<section class="dac-section dac-gray"><div class="wrap"> +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">リソース</h1> <div class="dac-section-subtitle"> - Android N 向けにアプリを開発する上で役立つ必須情報をご提供します。 + Android Nougat 向けにアプリを開発する上で役立つ必須情報をご提供します。 </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/ja/index.jd b/docs/html-intl/intl/ja/index.jd index 755ec6205bfe..ba73c4108627 100644 --- a/docs/html-intl/intl/ja/index.jd +++ b/docs/html-intl/intl/ja/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html-intl/intl/ko/about/versions/nougat/android-7.0-testing.jd b/docs/html-intl/intl/ko/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index 122222779242..000000000000 --- a/docs/html-intl/intl/ko/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=테스트 가이드 -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="tb-wrapper"> - <div id="tb"> - <h2>이 문서의 내용</h2> - <ol> - <li><a href="#runtime-permissions">권한 테스트</a></li> - <li><a href="#doze-standby">잠자기 및 앱 대기 모드 테스트</a></li> - <li><a href="#ids">자동 백업 및 기기 식별자</a></li> - </ol> - </div> -</div> - -<p> - Android N에서는 앱이 차기 버전의 플랫폼에서 제대로 작동하는지 확인해볼 수 있습니다. - 이 프리뷰에는 앱에 영향을 미칠 수 있는 수많은 API와 동작 변경 사항이 포함되어 있습니다. 이에 대해서는 <a href="{@docRoot}preview/api-overview.html">API 개요</a>와 <a href="{@docRoot}preview/behavior-changes.html">동작 변경 사항</a>에 설명되어 있습니다. - - 프리뷰로 앱을 테스트할 때에는 사용자에게 좋은 환경을 제공하기 위해 개발자 여러분이 꼭 초점을 맞춰야 하는 몇 가지 특정한 시스템 변경사항이 있습니다. - - -</p> - -<p> - 이 가이드에서는 앱에서 테스트할 프리뷰 기능은 어떤 것이고, 테스트 방법은 어떤지에 대해 설명합니다. 이와 같은 특정 프리뷰 기능을 먼저 테스트하는 것이 좋습니다. 왜냐하면 이들 기능은 앱의 동작에 큰 영향을 미칠 가능성이 높기 때문입니다. - - -</p> - -<ul> - <li><a href="#runtime-permissions">권한</a> - </li> - <li><a href="#doze-standby">잠자기 및 앱 대기 모드</a> - </li> - <li><a href="#ids">자동 백업 및 기기 식별자</a></li> -</ul> - -<p> - 테스트용 프리뷰 시스템 이미지로 기기 또는 가상 기기를 설정하는 방법에 대한 자세한 정보는 <a href="{@docRoot}preview/setup-sdk.html">Android N SDK 설정</a>을 참조하세요. - - -</p> - - -<h2 id="runtime-permissions">권한 테스트</h2> - -<p> - 새로운 <a href="{@docRoot}preview/features/runtime-permissions.html">권한</a> 모델은 사용자가 여러분의 앱에 권한을 할당하는 방법을 바꿔 놓습니다. - 설치 절차 중에 모든 권한을 허용하는 것이 아니라, 앱이 런타임에 사용자에게 각각의 권한을 요청해야 합니다. - - 사용자 입장에서는 이러한 동작으로 각 앱의 액티비티에 대해 더 세분화된 제어권을 행사할 수 있을 뿐만 아니라 이 앱이 어째서 특정한 권한을 요청하고 있는 것인지 맥락을 더 잘 이해할 수 있게 되기도 합니다. - 사용자는 언제든 앱에 개별적으로 권한을 허용할 수 있고, 이를 취소할 수도 있습니다. - 미리 보기의 이러한 기능은 앱의 동작에 영향을 미칠 가능성이 가장 높고, 앱의 몇 가지 기능이 작동하지 않도록 막거나 저하된 상태로 작동하게 할 수도 있습니다. - - -</p> - -<p class="caution"> - 이 변경 내용은 새 플랫폼에서 실행되는 모든 앱에 영향을 비치며, 새 플랫폼 버전을 대상으로 하지 않는 앱도 예외가 아닙니다. - 레거시 앱에 대해 플랫폼이 제한된 호환성 동작을 제공하기는 하지만, 지금 바로 새 권한 모델로 앱의 마이그레이션 계획을 시작하는 편이 좋습니다. 플랫폼이 공식적으로 출시될 때에 맞춰 앱의 업데이트된 버전을 게시하는 것을 목표로 하십시오. - - -</p> - - -<h3 id="permission-test-tips">테스트 팁</h3> - -<p> - 다음은 새 권한 동작에 대해 앱 테스트를 계획하고 실행하는 데 유용한 몇 가지 테스트 팁입니다. - -</p> - -<ul> - <li>앱의 현재 권한과 관련된 코드 경로를 확인합니다.</li> - <li>권한 보호된 서비스 및 데이터 전반에 걸친 사용자 흐름을 테스트합니다.</li> - <li>허용된/취소된 권한을 여러 가지로 조합하여 테스트합니다.</li> - <li>명령줄에서 권한을 관리할 때 {@code adb} 도구를 사용합니다. - <ul> - <li>권한과 상태를 그룹별로 목록으로 나열합니다. - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>하나 이상의 권한을 다음과 같은 구문을 사용하여 허용하거나 취소합니다.<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>권한을 사용하는 서비스에 대해 앱을 분석해봅니다.</li> -</ul> - -<h3 id="permission-test-strategy">테스트 전략</h3> - -<p> - 권한을 변경하면 앱의 구조와 디자인은 물론 사용자 환경과, 개발자가 사용자에게 제공하는 흐름에도 영향을 미칩니다. - 앱의 현재 권한 사용 내용을 평가한 다음 제공하고자 하는 새로운 흐름을 계획하기 시작해야 합니다. - 플랫폼의 공식 릴리스에서 호환성 동작을 제공할 예정이지만, 이와 같은 동작에만 의존하지 말고 앱 업데이트를 계획하는 것이 좋습니다. - - -</p> - -<p> - 앱이 실제로 필요로 하고 사용하는 권한을 확인한 다음, 권한 보호된 서비스를 사용하는 여러 가지 코드 경로를 찾습니다. - 이렇게 하려면 새 플랫폼에서 여러 가지로 조합한 테스트를 거치고 코드 분석을 통해야 합니다. - 테스트에서는 런타임 권한에 옵트인하는 것에 초점을 맞춰야 합니다. 이를 위해 앱의 {@code targetSdkVersion}을 프리뷰 버전으로 변경하세요. - 자세한 정보는 <a href="{@docRoot}preview/setup-sdk.html#">Android N SDK 설정</a>을 참조하세요. - - -</p> - -<p> - 다양한 조합의 권한을 해지하고 추가하는 방식으로 테스트를 수행하여 권한에 종속되는 사용자 흐름을 파악합니다. - 종속성이 분명하지 않거나 논리적인 경우, 리팩터링을 고려해 보거나 해당 흐름을 구분하여 종속성을 제거, 또는 해당 권한이 왜 필요한지 분명히 하는 방안을 고려해야 합니다. - - -</p> - -<p> - 런타임 권한의 동작, 테스트 및 모범 사례에 대한 자세한 정보는 <a href="{@docRoot}preview/features/runtime-permissions.html">권한</a> 개발자 미리 보기 페이지를 참조하십시오. - - -</p> - - -<h2 id="doze-standby">잠자기 및 앱 대기 모드 테스트</h2> - -<p> - 잠자기 및 앱 대기 모드의 절전 기능은 기기가 유휴 상태에 있을 때 또는 사용자가 앱에 초점을 맞추고 있지 않을 때 앱이 수행할 수 있는 배경 처리의 양을 제한합니다. - 시스템이 앱에 부과할 수 있는 제한 사항에는 네트워크 액세스를 제한하거나 없애기, 배경 작업을 일시 중지시키기, 알림 일시 중지, 절전 모드 해제 및 알람 요청 무시 등이 포함됩니다. - - 이러한 절전 기능에 앱이 적절히 동작하도록 확실히 해 두려면 이와 같은 저전력 상태를 시뮬레이트하여 앱을 테스트해보아야 합니다. - - -</p> - -<h4 id="doze">앱에서 잠자기 모드 테스트하기</h4> - -<p>앱에서 잠자기 모드를 테스트하려면:</p> - -<ol> -<li>Android N 시스템 이미지로 하드웨어 기기 또는 가상 기기를 구성합니다.</li> -<li>기기를 개발 머신에 연결하고 앱을 설치합니다.</li> -<li>앱을 실행시킨 다음 활성 상태로 그냥 둡니다.</li> -<li>다음 명령을 실행하여 기기가 잠자기 모드에 들어가는 것을 시뮬레이션합니다. - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>기기가 다시 활성화되면 앱이 어떻게 동작하는지 살펴봅니다. 기기가 잠자기 모드를 종료할 때 정상적으로 복구되는지 확인해야 합니다. -</li> -</ol> - - -<h4 id="standby">앱에서 앱 대기 모드 테스트하기</h4> - -<p>앱에서 앱 대기 모드를 테스트하려면:</p> - -<ol> - <li>Android N 시스템 이미지로 하드웨어 기기 또는 가상 기기를 구성합니다.</li> - <li>기기를 개발 머신에 연결하고 앱을 설치합니다.</li> - <li>앱을 실행시킨 다음 활성 상태로 그냥 둡니다.</li> - <li>다음 명령을 실행하여 앱이 대기 모드에 들어가는 것을 시뮬레이션합니다. - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>다음 명령을 사용하여 앱이 대기 모드에서 해제되는 것을 시뮬레이션합니다. - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>앱이 대기 모드에서 해제된 상태에서 어떻게 동작하는지 살펴봅니다. 대기 모드에서 정상적으로 복구되는지 확인해야 합니다. - 특히, 앱의 알림과 배경 작업이 계속 예상했던 대로 기능하는지 확인해야 합니다. -</li> -</ol> - -<h2 id="ids">앱용 자동 백업 및 기기별 식별자</h2> - -<p>앱이 내부 저장소에서 각 기기에 따라 다른 식별자(예: Google Cloud Messaging 등록 ID)를 유지하는 경우, 모범 사례를 따라 저장소 위치를 자동 백업에서 배제해야 합니다. 이 내용은 <a href="{@docRoot}preview/backup/index.html">앱용 자동 백업</a>에 설명되어 있습니다. - - - - </p> diff --git a/docs/html-intl/intl/ko/about/versions/nougat/index.jd b/docs/html-intl/intl/ko/about/versions/nougat/index.jd index 4b0ccc594670..6ed065be0054 100644 --- a/docs/html-intl/intl/ko/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/ko/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,62 +17,56 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - Android N을 맞이할 준비를 하세요! + Android Nougat을 맞이할 준비를 하세요! Nexus와 다른 기기에서 <strong>앱을 테스트하세요</strong>. <strong>전력과 메모리를 절약</strong>하는 새로운 시스템 동작을 지원하세요. <strong>다중 창 UI</strong>, <strong>직접 회신 알림</strong> 등으로 앱을 확장하세요. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> 시작하기 - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> 문제 보고 </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - 릴리스 노트 보기 - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> 개발자 커뮤니티 가입 @@ -113,19 +107,34 @@ footer.hide=1 data-initial-results="3"></div> </div></section> -<section class="dac-section dac-gray"><div class="wrap"> +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">리소스</h1> <div class="dac-section-subtitle"> - 앱을 Android N에서 사용할 수 있도록 준비하는 데 유용한 중요 정보입니다. + 앱을 Android Nougat에서 사용할 수 있도록 준비하는 데 유용한 중요 정보입니다. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/ko/index.jd b/docs/html-intl/intl/ko/index.jd index 01c85873c6c6..e1024119e034 100644 --- a/docs/html-intl/intl/ko/index.jd +++ b/docs/html-intl/intl/ko/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html-intl/intl/pt-br/about/versions/nougat/android-7.0-testing.jd b/docs/html-intl/intl/pt-br/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index c00fd21bf187..000000000000 --- a/docs/html-intl/intl/pt-br/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=Guia de teste -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="tb-wrapper"> - <div id="tb"> - <h2>Neste documento</h2> - <ol> - <li><a href="#runtime-permissions">Teste de permissões</a></li> - <li><a href="#doze-standby">Teste de soneca e App em espera</a></li> - <li><a href="#ids">Identificadores de dispositivo e backup automático</a></li> - </ol> - </div> -</div> - -<p> - O Android N fornece uma oportunidade de garantir que os aplicativos funcionem - na próxima versão da plataforma. Esta prévia inclui uma série de mudanças de comportamento e APIs que podem - ter impacto no aplicativo, como descrito em <a href="{@docRoot}preview/api-overview.html">Visão geral da API -</a> e <a href="{@docRoot}preview/behavior-changes.html">Mudanças de comportamento</a>. No teste - do aplicativo com a prévia, há algumas alterações de sistema específicas em que você deve se concentrar - para garantir que os usuários tenham uma boa experiência. -</p> - -<p> - Este guia descreve quais recursos de prévia testar e como testá-los com o aplicativo. Você deve - priorizar o teste destes recursos de prévia específicos devido ao grande impacto potencial no - comportamento do aplicativo: -</p> - -<ul> - <li><a href="#runtime-permissions">Permissões</a> - </li> - <li><a href="#doze-standby">Soneca e App em espera</a> - </li> - <li><a href="#ids">Identificadores de dispositivo e backup automático</a></li> -</ul> - -<p> - Para obter mais informações sobre como configurar dispositivos físicos ou virtuais com uma imagem do sistema de prévia - para teste, consulte <a href="{@docRoot}preview/setup-sdk.html">Configuração -do Android N SDK</a>. -</p> - - -<h2 id="runtime-permissions">Teste de permissões</h2> - -<p> - O novo modelo de <a href="{@docRoot}preview/features/runtime-permissions.html">permissões</a> - altera a maneira que as permissões são alocadas ao aplicativo pelo usuário. Em vez de conceder todas as permissões - durante o procedimento de instalação, o aplicativo deve pedir ao usuário permissões individuais - em tempo de execução. Para os usuários, este comportamento fornece um controle mais granular sobre as atividades de cada aplicativo, bem - como um melhor contexto para entender o porquê do aplicativo estar solicitando uma permissão específica. Os usuários - podem conceder ou revogar as permissões concedidas a um aplicativo individualmente a qualquer momento. É provável que este recurso - da prévia tenha um impacto no comportamento do aplicativo e pode impedir que alguns - dos recursos do aplicativo funcionem, ou funcionem em um estado degradado. -</p> - -<p class="caution"> - Esta alteração afeta todos os aplicativos em execução na nova plataforma, mesmo aqueles que não são destinados - para a versão nova da plataforma. A plataforma fornece um comportamento de compatibilidade limitado para aplicativos legados. No entanto, - você deve começar a planejar a migração do aplicativo para o novo modelo de permissões agora, com o objetivo - de publicar uma versão atualizada do aplicativo no lançamento oficial da plataforma. -</p> - - -<h3 id="permission-test-tips">Dicas de teste</h3> - -<p> - Use as seguintes dicas de teste para ajudar você a planejar e executar os testes do aplicativo com o novo - comportamento de permissões. -</p> - -<ul> - <li>Identifique as permissões atuais do aplicativo e os caminhos de código relacionados.</li> - <li>Teste o fluxo de usuário entre serviços protegidos por permissão e dados.</li> - <li>Teste com várias combinações de permissões revogadas/concedidas.</li> - <li>Use a ferramenta {@code adb} para gerenciar as permissões da linha de comando: - <ul> - <li>Liste as permissões e o status por grupos: - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>Conceda ou revogue uma ou mais permissões usando a seguinte sintaxe:<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>Analise o aplicativo para encontrar os serviços que usam permissões.</li> -</ul> - -<h3 id="permission-test-strategy">Estratégia de teste</h3> - -<p> - A mudança de permissões afeta a estrutura e o projeto do aplicativo, bem como - a experiência do usuário e os fluxos fornecidos a eles. Você deve avaliar o uso das permissões atuais - do aplicativo e começar a planejar novos fluxos que deseja oferecer. O lançamento oficial - da plataforma fornece comportamento de compatibilidade, mas deve-se planejar a atualização do aplicativo e - não confiar nestes comportamentos. -</p> - -<p> - Identifique as permissões que o aplicativo realmente precisa e usa e, em seguida, encontre os vários caminhos - de código que usam os serviços protegidos por permissões. É possível fazer isto por meio de uma combinação de - testes na nova plataforma e análise de códigos. Nos testes, você deve se concentrar em usar - as permissões em tempo de execução alterando {@code targetSdkVersion} do aplicativo para a versão da prévia. Para - obter mais informações, consulte <a href="{@docRoot}preview/setup-sdk.html#">Configuração -do Android N SDK</a>. -</p> - -<p> - Teste com várias combinações de permissões revogadas e concedidas para destacar os fluxos de usuário -que dependem de permissões. Onde uma dependência não for óbvia ou lógica, considere -refatorar ou compartimentalizar este fluxo para eliminar a dependência ou para esclarecer por que -a permissão é necessária. -</p> - -<p> - Para obter mais informações sobre o comportamento das permissões em tempo de execução, de testes e de melhores práticas, consulte a página - <a href="{@docRoot}preview/features/runtime-permissions.html">Permissões</a> do Developer - Preview. -</p> - - -<h2 id="doze-standby">Teste de soneca e App em espera</h2> - -<p> - Os recursos de economia de energia de App em espera e soneca limitam a quantidade de processamento de segundo plano que o aplicativo - pode realizar quando um dispositivo está no estado ocioso ou enquanto não está em foco. As - restrições que o sistema pode impor nos aplicativos inclui acesso a rede limitado ou restrito, - tarefas de segundo plano suspensas, notificações suspensas, solicitações de soneca ignoradas e despertadores. Para garantir - que o aplicativo se comportará adequadamente com essas otimizações de economia de energia, deve-se testá-lo - simulando estes estados de baixa energia. -</p> - -<h4 id="doze">Testar o aplicativo com Soneca</h4> - -<p>Para testar a Soneca com o aplicativo:</p> - -<ol> -<li>Configure um dispositivo de hardware ou virtual com uma imagem do sistema Android N.</li> -<li>Conecte o dispositivo à máquina de desenvolvimento e instale o aplicativo.</li> -<li>Execute o aplicativo e deixe-o ativo.</li> -<li>Simule o dispositivo acessando o modo Soneca executando os seguintes comandos: - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>Observe o comportamento do aplicativo quando o dispositivo é reativado. Certifique-se de que - ele se recupere corretamente quando o dispositivo sai do modo Soneca.</li> -</ol> - - -<h4 id="standby">Testar aplicativos com App em espera</h4> - -<p>Para testar o modo de espera do aplicativo:</p> - -<ol> - <li>Configure um dispositivo de hardware ou virtual com uma imagem do sistema Android N.</li> - <li>Conecte o dispositivo à máquina de desenvolvimento e instale o aplicativo.</li> - <li>Execute o aplicativo e deixe-o ativo.</li> - <li>Simule o aplicativo acessando o modo de espera executando os seguintes comandos: - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>Simule o despertar do aplicativo usando o seguinte comando: - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>Observe o comportamento do aplicativo quando ele é despertado. Certifique-se de que ele se recupere corretamente - do modo de espera. Particularmente, deve-se verificar se as notificações e os trabalho de segundo plano - do aplicativo continuam a funcionar como o esperado.</li> -</ol> - -<h2 id="ids">Backup automático para aplicativos e identificadores específicos do dispositivo</h2> - -<p>Caso o aplicativo esteja persistindo qualquer identificador específico do dispositivo, como o ID de registro do Google -Cloud Messaging, no armazenamento interno, -certifique-se de seguir as práticas recomendadas para excluir o local de armazenamento -do backup automático, como descrito em <a href="{@docRoot}preview/backup/index.html">Backup automático -para aplicativos</a>. </p> diff --git a/docs/html-intl/intl/pt-br/about/versions/nougat/index.jd b/docs/html-intl/intl/pt-br/about/versions/nougat/index.jd index 58b24088b0d3..c7aee2ad5ba9 100644 --- a/docs/html-intl/intl/pt-br/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/pt-br/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,62 +17,56 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - Prepare-se para o Android N! + Prepare-se para o Android Nougat! <strong>Teste os aplicativos</strong> no Nexus e outros dispositivos. Ofereça suporte aos novos comportamentos do sistema para <strong>economizar energia e memória</strong>. Estenda seus aplicativos com a <strong>IU de várias janelas</strong>, <strong>direcione notificações de resposta</strong> e muito mais. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> Primeiros passos - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Informe um problema </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - Veja as notas de versão - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Participe da comunidade de desenvolvedores @@ -82,26 +76,6 @@ footer.hide=1 </div><!-- end .wrap --> </div><!-- end .dac-actions --> -<div id="useOldTemplates" style="display:none;color:black" class="actions-bar dac-expand dac-invert"> - <div class="wrap dac-offset-parent"> - - <div class="actions"> - <div><a href="https://developer.android.com/preview/bug"> - <span class="dac-sprite dac-auto-chevron-large"></span> - Informe um problema - </a></div> - <div><a href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron-large"></span> - Veja as notas de versão - </a></div> - <div><a href="{@docRoot}preview/dev-community"> - <span class="dac-sprite dac-auto-chevron-large"></span> - Participe da comunidade de desenvolvedores - </a></div> - </div><!-- end .actions --> - </div><!-- end .wrap --> -</div> - <section class="dac-section dac-light dac-small" id="latest"><div class="wrap"> <h2 class="norule">Mais recente</h2> <div class="resource-widget resource-flow-layout col-16" @@ -113,19 +87,33 @@ footer.hide=1 data-initial-results="3"></div> </div></section> -<section class="dac-section dac-gray"><div class="wrap"> +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">Recursos</h1> <div class="dac-section-subtitle"> - Informações essenciais para ajudar você a preparar seus aplicativos para o Android N. + Informações essenciais para ajudar você a preparar seus aplicativos para o Android Nougat. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/pt-br/index.jd b/docs/html-intl/intl/pt-br/index.jd index f5e1569dc846..3c8f75dc94a9 100644 --- a/docs/html-intl/intl/pt-br/index.jd +++ b/docs/html-intl/intl/pt-br/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html-intl/intl/ru/about/versions/nougat/index.jd b/docs/html-intl/intl/ru/about/versions/nougat/index.jd index 9bb56baccde8..1103166e74a7 100644 --- a/docs/html-intl/intl/ru/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/ru/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="предварительная версия", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,62 +17,56 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - Подготовьтесь к выходу Android N! + Подготовьтесь к выходу Android Nougat! <strong>Протестируйте свои приложения</strong> на Nexus и других устройствах. Поддержите нововведения системы, позволяющие <strong>снизить потребление энергии и памяти</strong>. Добавьте в свои приложения <strong>многооконный режим</strong>, <strong>возможность прямой отправки ответов из уведомлений</strong> и другие функции. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> Начало работы - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button="" href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Сообщить о проблеме </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - См. примечания к выпуску - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Вступить в сообщество разработчиков @@ -102,19 +96,45 @@ footer.hide=1 </div><!-- end .wrap --> </div> -<section class="dac-section dac-light"><div class="wrap"> +<section class="dac-section dac-light dac-small" id="latest"><div class="wrap"> + <h2 class="norule">Latest</h2> + <div class="resource-widget resource-flow-layout col-16" + data-query="type:blog+tag:androidn+tag:featured, type:youtube+tag:androidn+tag:featured" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"></div> +</div></section> + +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">Ресурсы</h1> <div class="dac-section-subtitle"> - Важная информация, которая поможет вам подготовить ваши приложения для работы в Android N. + Важная информация, которая поможет вам подготовить ваши приложения для работы в Android Nougat. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/ru/index.jd b/docs/html-intl/intl/ru/index.jd index e917a8dba789..b3f3cc207092 100644 --- a/docs/html-intl/intl/ru/index.jd +++ b/docs/html-intl/intl/ru/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html-intl/intl/vi/about/versions/nougat/index.jd b/docs/html-intl/intl/vi/about/versions/nougat/index.jd index bd64b250fb37..58b4b5f8bebe 100644 --- a/docs/html-intl/intl/vi/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/vi/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,62 +17,57 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - Hãy chuẩn bị sẵn sàng cho Android N! + Hãy chuẩn bị sẵn sàng cho Android Nougat! <strong>Kiểm thử ứng dụng của bạn</strong> trên Nexus và các thiết bị khác. Hỗ trợ các hành vi hệ thống mới nhằm <strong>tiết kiệm năng lượng và bộ nhớ</strong>. Mở rộng ứng dụng của bạn bằng <strong>UI đa cửa sổ</strong>, <strong>thông báo trả lời trực tiếp</strong> và nhiều tính năng khác. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> Bắt đầu - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> + <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button="" href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Báo cáo vấn đề </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - Xem ghi chú phát hành - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Tham gia cộng đồng nhà phát triển @@ -102,19 +97,44 @@ footer.hide=1 </div><!-- end .wrap --> </div> -<section class="dac-section dac-light"><div class="wrap"> +<section class="dac-section dac-light dac-small" id="latest"><div class="wrap"> + <h2 class="norule">Latest</h2> + <div class="resource-widget resource-flow-layout col-16" + data-query="type:blog+tag:androidn+tag:featured, type:youtube+tag:androidn+tag:featured" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"></div> +</div></section> + +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">Tài nguyên</h1> <div class="dac-section-subtitle"> - Các thông tin cần thiết để trợ giúp bạn chuẩn bị cho ứng dụng sẵn sàng chạy trên Android N. + Các thông tin cần thiết để trợ giúp bạn chuẩn bị cho ứng dụng sẵn sàng chạy trên Android Nougat. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/zh-cn/about/versions/nougat/android-7.0-testing.jd b/docs/html-intl/intl/zh-cn/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index d1e118741cff..000000000000 --- a/docs/html-intl/intl/zh-cn/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=测试指南 -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="tb-wrapper"> - <div id="tb"> - <h2>本文内容</h2> - <ol> - <li><a href="#runtime-permissions">测试权限</a></li> - <li><a href="#doze-standby">测试低电耗模式和应用待机模式</a></li> - <li><a href="#ids">自动备份和设备标识符</a></li> - </ol> - </div> -</div> - -<p> - 利用 Android N,您有机会确保应用可使用下一平台版本。 -如 <a href="{@docRoot}preview/api-overview.html">API 概览</a>和<a href="{@docRoot}preview/behavior-changes.html">行为变更</a>中所述,该 Preview 包括大量 API 和可能影响应用的行为变更。 - -使用 Preview 测试应用时,您应重点关注一些特定的系统变更,确保用户拥有愉悦的体验。 - - -</p> - -<p> - 本指南介绍可使用您的应用测试 Preview 的哪些功能以及如何测试。您应确定优先测试以下特定 Preview 功能,因为它们可能会对应用行为产生较大影响。 - - -</p> - -<ul> - <li><a href="#runtime-permissions">权限</a> - </li> - <li><a href="#doze-standby">低电耗模式和应用待机模式</a> - </li> - <li><a href="#ids">自动备份和设备标识符</a></li> -</ul> - -<p> - 如需了解有关如何使用 Preview 系统映像设置设备或虚拟设备以进行测试的详细信息,请参阅<a href="{@docRoot}preview/setup-sdk.html">设置 Android N SDK</a>。 - - -</p> - - -<h2 id="runtime-permissions">测试权限</h2> - -<p> - 新<a href="{@docRoot}preview/features/runtime-permissions.html">权限</a>模型改变了用户向您的应用分配权限的方式。 -您的应用必须在运行时要求用户提供各项权限,而不是在安装过程中要求授予所有权限。 - -对于用户而言,此行为有助于他们更精细地控制每个应用的 Activity,并更深入地了解应用为何请求提供特定权限的上下文信息。 -用户可以随时向应用授予某项权限或撤销其某项权限。 -预览版的这种功能最有可能会对应用行为产生影响,而且可能会阻止某些应用功能运行或只能在降级状态中运行。 - - -</p> - -<p class="caution"> - 这一变更会影响在新平台上运行的所有应用,即便这些应用并非面向新平台版本开发亦是如此。 -该平台为旧版应用提供有限的兼容性行为,但您现在应当开始计划将应用迁移到新权限模型,以便在官方平台启动时发布更新的应用版本。 - - -</p> - - -<h3 id="permission-test-tips">测试提示</h3> - -<p> - 使用以下测试提示有助于您计划并通过新权限行为执行应用测试。 - -</p> - -<ul> - <li>识别应用的当前权限和相关的代码路径</li> - <li>跨受权限保护的服务和数据测试用户流程</li> - <li>使用授予/撤销权限的各种组合进行测试</li> - <li>使用 {@code adb} 工具从命令行管理权限: - <ul> - <li>按组列出权限和状态: - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>使用以下语法授予或撤销一项或多项权限:<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>针对使用权限的服务对应用进行分析</li> -</ul> - -<h3 id="permission-test-strategy">测试策略</h3> - -<p> - 权限更改会影响应用的结构和设计,以及您为用户提供的用户体验和流程。 -您应评估应用的当前权限使用情况并开始计划要提供的新流程。 -平台的正式版本提供兼容性行为,但您应计划更新应用,而不是依赖于这些行为。 - - -</p> - -<p> - 确定应用实际需要和使用的权限,然后找出各种使用受权限保护的服务的代码路径。 -您可通过结合使用新平台测试和代码分析完成此操作。 -在测试中,您应通过将应用的 {@code targetSdkVersion} 更改为预览版,重点关注选择运行时权限。 -如需了解详细信息,请参阅<a href="{@docRoot}preview/setup-sdk.html#">设置 Android N SDK</a>。 - - -</p> - -<p> - 使用已撤销和已添加权限的各种组合进行测试,突出显示依赖于权限的用户流程。 -如果依赖关系不明显或不符合逻辑,则您应考虑重构或划分该流程,以消除依赖关系或阐明需要权限的原因。 - - -</p> - -<p> - 如需了解有关运行时权限行为、测试和最佳做法的详细信息,请参阅<a href="{@docRoot}preview/features/runtime-permissions.html">权限</a>开发者预览版页面。 - - -</p> - - -<h2 id="doze-standby">测试低电耗模式和应用待机模式</h2> - -<p> - 当设备处于空闲状态或应用未聚焦时,低电耗模式和应用待机模式的节能功能将限制应用可执行的后台处理工作量。 -系统可对应用实施的限制包括:限制或禁止访问网络、暂停后台任务、暂停通知、忽略唤醒请求和闹铃。 - -要确保应用在完成这些节能优化后正常运行,您应通过模拟这些低功耗状态对应用进行测试。 - - -</p> - -<h4 id="doze">在低电耗模式下测试您的应用</h4> - -<p>要在低电耗模式下测试您的应用,请执行以下操作:</p> - -<ol> -<li>使用 Android N 系统映像配置硬件设备或虚拟设备</li> -<li>将设备连接到开发计算机并安装应用</li> -<li>运行应用并使其保持活动状态</li> -<li>通过运行以下命令,模拟进入低电耗模式的设备: - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>观察重新激活设备时的应用行为。确保应用在设备退出低电耗模式时正常恢复 -</li> -</ol> - - -<h4 id="standby">在应用待机模式下测试您的应用</h4> - -<p>要在应用待机模式下测试您的应用,请执行以下操作:</p> - -<ol> - <li>使用 Android N 系统映像配置硬件设备或虚拟设备</li> - <li>将设备连接到开发计算机并安装应用</li> - <li>运行应用并使其保持活动状态</li> - <li>通过运行以下命令,模拟进入待机模式的应用: - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>使用以下命令模拟如何唤醒应用: - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>观察唤醒后的应用行为。确保应用从待机模式中正常恢复。 -特别地,您应检查应用的通知和后台作业是否按预期继续运行 -</li> -</ol> - -<h2 id="ids">自动备份应用和设备特定的标识符</h2> - -<p>如果应用坚持在内部存储中使用任何设备特定的标识符,如 Google 云消息传递注册 ID,请确保遵循最佳做法将存储位置从自动备份中排除,如<a href="{@docRoot}preview/backup/index.html">自动备份应用</a>中所述。 - - - - </p> diff --git a/docs/html-intl/intl/zh-cn/about/versions/nougat/index.jd b/docs/html-intl/intl/zh-cn/about/versions/nougat/index.jd index 74cd0391aefd..c1eb4234d83e 100644 --- a/docs/html-intl/intl/zh-cn/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/zh-cn/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,62 +17,57 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - 为 Android N 进行准备! + 为 Android Nougat 进行准备! 在 Nexus 和其他设备上<strong>测试您的应用</strong>。支持新系统行为以<strong>节省电量和内存</strong>。 使用<strong>多窗口 UI</strong> 扩展您的应用,以便能够<strong>直接答复通知</strong>及执行其他操作。 </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> 入门指南 - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> + <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> 报告问题 </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - 查阅版本说明 - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> 加入开发者社区 @@ -113,19 +108,33 @@ footer.hide=1 data-initial-results="3"></div> </div></section> -<section class="dac-section dac-gray"><div class="wrap"> +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">资源</h1> <div class="dac-section-subtitle"> - 这些必备信息可帮助您的应用为Android N做好准备。 + 这些必备信息可帮助您的应用为Android Nougat做好准备。 </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/zh-cn/index.jd b/docs/html-intl/intl/zh-cn/index.jd index ca3a84b80eb2..8872d16b0714 100644 --- a/docs/html-intl/intl/zh-cn/index.jd +++ b/docs/html-intl/intl/zh-cn/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html-intl/intl/zh-tw/about/versions/nougat/index.jd b/docs/html-intl/intl/zh-tw/about/versions/nougat/index.jd index e899bc0b1260..d4db467ca324 100644 --- a/docs/html-intl/intl/zh-tw/about/versions/nougat/index.jd +++ b/docs/html-intl/intl/zh-tw/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,66 +17,61 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> + <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - 為 Android N 做好準備! + 為 Android Nougat 做好準備! 在 Nexus 與其他裝置上<strong>測試您的應用程式</strong>。支援新系統行為以<strong>節省電力與記憶體</strong>。使用<strong>多視窗 UI</strong>、<strong>直接回覆通知</strong>等延伸您的應用程式。 </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> - 開始使用</a> -<!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + 開始使用 + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" + srcset="{@docRoot}images/home/n-preview-hero.png 1x, + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" + data-query="collection:nougat/landing/resources" data-cardSizes="6x2" - data-maxResults="6"></div> - </div> + data-maxResults="3"></div> + </div> </div> </section> + <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> - <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> + <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> 回報問題 -</a> - </li> - <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - 查看版本資訊</a> - + </a> </li> <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - 加入開發人員社群</a> - + 加入開發人員社群 + </a> </li> </ul> </div><!-- end .wrap --> @@ -102,19 +97,44 @@ footer.hide=1 </div><!-- end .wrap --> </div> -<section class="dac-section dac-light"><div class="wrap"> +<section class="dac-section dac-light dac-small" id="latest"><div class="wrap"> + <h2 class="norule">Latest</h2> + <div class="resource-widget resource-flow-layout col-16" + data-query="type:blog+tag:androidn+tag:featured, type:youtube+tag:androidn+tag:featured" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"></div> +</div></section> + +<section class="dac-section dac-gray" id="videos"><div class="wrap"> + <h1 class="dac-section-title">Videos</h1> + <div class="dac-section-subtitle"> + New Android capabilities and the right way to use them in your apps. + </div> + + <div class="resource-widget resource-flow-layout col-16" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="6" + data-maxResults="15" + data-initial-results="3"> + </div> +</div></section> + +<section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">資源</h1> <div class="dac-section-subtitle"> - 以下重要資訊可幫助您的應用程式準備好使用 Android N。 + 以下重要資訊可幫助您的應用程式準備好使用 Android Nougat。 </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html-intl/intl/zh-tw/index.jd b/docs/html-intl/intl/zh-tw/index.jd index a5772ef0fab0..540801a53d4f 100644 --- a/docs/html-intl/intl/zh-tw/index.jd +++ b/docs/html-intl/intl/zh-tw/index.jd @@ -15,33 +15,30 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index a1581144e56c..aefe1c1f1ab5 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -25,6 +25,8 @@ redirects: to: /topic/libraries/support-library/index.html - from: /billions to: /topic/billions/index.html +- from: /performance + to: /topic/performance/index.html - from: /tools/extras/support-library.html to: /topic/libraries/support-library/index.html - from: /training/basics/fragments/support-lib.html @@ -800,13 +802,13 @@ redirects: - from: /preview/dev-community to: https://plus.google.com/communities/103655397235276743411 - from: /preview/bug - to: https://code.google.com/p/android/issues/entry?template=Developer%20preview%20report + to: https://source.android.com/source/report-bugs.html - from: /preview/bug/ - to: https://code.google.com/p/android/issues/entry?template=Developer%20preview%20report + to: https://source.android.com/source/report-bugs.html - from: /preview/bugreport - to: https://code.google.com/p/android/issues/entry?template=Developer%20preview%20report + to: https://source.android.com/source/report-bugs.html - from: /preview/bugreport/ - to: https://code.google.com/p/android/issues/entry?template=Developer%20preview%20report + to: https://source.android.com/source/report-bugs.html - from: /preview/bugs to: https://code.google.com/p/android/issues/list?can=2&q=label%3ADevPreview-N - from: /preview/bugs/ @@ -1250,7 +1252,7 @@ redirects: - from: /preview/samples.html to: /about/versions/nougat/android-7.0-samples.html - from: /preview/guide.html - to: /about/versions/nougat/android-7.0-testing.html + to: /about/versions/nougat/index.html - from: /preview/api-overview.html to: /about/versions/nougat/android-7.0.html - from: /preview/index.html diff --git a/docs/html/about/_book.yaml b/docs/html/about/_book.yaml index 958435d5e894..642113b3fe3d 100644 --- a/docs/html/about/_book.yaml +++ b/docs/html/about/_book.yaml @@ -2,14 +2,67 @@ toc: - title: Nougat path: /about/versions/nougat/index.html section: - - title: Android 7.0 APIs + - title: Android 7.0 for Developers path: /about/versions/nougat/android-7.0.html - - title: Android 7.0 Changes + path_attributes: + - name: es-lang + value: Información general de la API + - name: in-lang + value: Android N untuk Pengembang + - name: ja-lang + value: API の概要 + - name: ko-lang + value: API 개요 + - name: pt-br-lang + value: Visão geral da API + - name: ru-lang + value: Обзор API-интерфейсов + - name: vi-lang + value: Android N cho Nhà phát triển + - name: zh-cn-lang + value: API 概览 + - name: zh-tw-lang + value: API 總覽 + - title: Android 7.0 Behavior Changes path: /about/versions/nougat/android-7.0-changes.html + path_attributes: + - name: es-lang + value: Cambios en los comportamientos + - name: in-lang + value: Perubahan Perilaku + - name: ja-lang + value: 動作の変更点 + - name: ko-lang + value: 동작 변경 + - name: pt-br-lang + value: Mudanças de comportamento + - name: ru-lang + value: Изменения в работе + - name: vi-lang + value: Các thay đổi Hành vi + - name: zh-cn-lang + value: 行为变更 + - name: zh-tw-lang + value: 行為變更 - title: Android 7.0 Samples path: /about/versions/nougat/android-7.0-samples.html - - title: Android 7.0 Testing - path: /about/versions/nougat/android-7.0-testing.html + path_attributes: + - name: es-lang + value: Ejemplos + - name: in-lang + value: Contoh + - name: ja-lang + value: サンプル + - name: ko-lang + value: 샘플 + - name: pt-br-lang + value: Exemplos + - name: ru-lang + value: Примеры + - name: zh-cn-lang + value: 示例 + - name: zh-tw-lang + value: 範例 - title: Marshmallow path: /about/versions/marshmallow/index.html diff --git a/docs/html/about/about_toc.cs b/docs/html/about/about_toc.cs index a45601278d88..de396208ef2c 100644 --- a/docs/html/about/about_toc.cs +++ b/docs/html/about/about_toc.cs @@ -1,5 +1,17 @@ <ul id="nav"> <li class="nav-section"> + <div class="nav-section-header"><a href="<?cs var:toroot ?>about/versions/nougat/index.html"> + <span class="en">Nougat</span></a></div> + <ul> + <li><a href="<?cs var:toroot ?>about/versions/nougat/android-7.0.html"> + Android 7.0 for Developers</a></li> + <li><a href="<?cs var:toroot ?>about/versions/nougat/android-7.0-changes.html"> + Android 7.0 Changes</a></li> + <li><a href="<?cs var:toroot ?>about/versions/nougat/android-7.0-samples.html"> + Android 7.0 Samples</a></li> + </ul> + </li> + <li class="nav-section"> <div class="nav-section-header"><a href="<?cs var:toroot ?>about/versions/marshmallow/index.html"> <span class="en">Marshmallow</span></a></div> <ul> diff --git a/docs/html/about/versions/marshmallow/index.jd b/docs/html/about/versions/marshmallow/index.jd index 35edd72a38ec..93d0fec012db 100644 --- a/docs/html/about/versions/marshmallow/index.jd +++ b/docs/html/about/versions/marshmallow/index.jd @@ -2,7 +2,6 @@ page.title=Android 6.0 Marshmallow page.tags="marshmallow" meta.tags="marshamallow","android60" fullpage=true -nonavpage=true forcelocalnav=true header.hide=1 footer.hide=1 diff --git a/docs/html/about/versions/nougat/android-7.0-changes.jd b/docs/html/about/versions/nougat/android-7.0-changes.jd index 4f9054c7d526..84a56d0614c5 100644 --- a/docs/html/about/versions/nougat/android-7.0-changes.jd +++ b/docs/html/about/versions/nougat/android-7.0-changes.jd @@ -1,7 +1,7 @@ -page.title=Android 7.0 Changes +page.title=Android 7.0 Behavior Changes page.keywords=preview,sdk,compatibility -meta.tags="Android 7.0", "Nougat", "android n", "compatibility" -page.tags="Android 7.0", "Nougat", "android n", "developer preview" +meta.tags="Nougat", "android n", "compatibility" +page.tags="Android 7.0", "Nougat", "android n" page.image=images/cards/card-n-changes_2x.png @jd:body @@ -35,16 +35,14 @@ page.image=images/cards/card-n-changes_2x.png <h2>API Differences</h2> <ol> - <li><a href="{@docRoot}sdk/api_diff/n-preview-4-incr/changes.html"> - Preview 3 to API 24</a></li> <li><a href="{@docRoot}sdk/api_diff/24/changes.html"> API 23 to API 24</a></li> </ol> <h2>See Also</h2> <ol> - <li><a href="{@docRoot}preview/api-overview.html"> - Android 7.0 APIs</a></li> + <li><a href="{@docRoot}about/versions/nougat/android-7.0.html"> + Android 7.0 for Developers</a></li> </ol> </div> diff --git a/docs/html/about/versions/nougat/android-7.0-samples.jd b/docs/html/about/versions/nougat/android-7.0-samples.jd index 98f7090bbb22..e283a7a22f83 100644 --- a/docs/html/about/versions/nougat/android-7.0-samples.jd +++ b/docs/html/about/versions/nougat/android-7.0-samples.jd @@ -1,10 +1,11 @@ page.title=Android 7.0 Samples page.tags="Android 7.0", "nougat", "samples", "android" +meta.tags="android n", "Nougat", "android 7.0" page.image=images/cards/card-n-samples_2x.png @jd:body <p> - The following code samples are provided for Android 7.0. To + Use the code samples below to learn about Android 7.0 capabilities and APIs. To download the samples in Android Studio, select the <b>File > Import Samples</b> menu option. </p> diff --git a/docs/html/about/versions/nougat/android-7.0-testing.jd b/docs/html/about/versions/nougat/android-7.0-testing.jd deleted file mode 100644 index f30382e0decc..000000000000 --- a/docs/html/about/versions/nougat/android-7.0-testing.jd +++ /dev/null @@ -1,190 +0,0 @@ -page.title=Android 7.0 Testing Guide -page.image=images/cards/card-n-guide_2x.png -meta.tags="preview", "testing" -page.tags="preview", "developer preview" - -@jd:body - -<div id="qv-wrapper"> - <div id="qv"> - <h2>In this document</h2> - <ol> - <li><a href="#runtime-permissions">Testing Permissions</a></li> - <li><a href="#doze-standby">Testing Doze and App Standby</a></li> - <li><a href="#ids">Auto Backup and Device Identifiers</a></li> - </ol> - </div> -</div> - -<p> - Android 7.0 gives you an opportunity to ensure your apps work with the next - version of the platform. This preview includes a number of APIs and behavior changes that can - impact your app, as described in the <a href="{@docRoot}preview/api-overview.html">API - Overview</a> and <a href="{@docRoot}preview/behavior-changes.html">Behavior Changes</a>. In testing - your app with the preview, there are some specific system changes that you should focus on to - ensure that users have a good experience. -</p> - -<p> - This guide describes the what and how to test preview features with your app. You should - prioritize testing of these specific preview features, due to their high potential impact on your - app's behavior: -</p> - -<ul> - <li><a href="#runtime-permissions">Permissions</a> - </li> - <li><a href="#doze-standby">Doze and App Standby</a> - </li> - <li><a href="#ids">Auto Backup and Device Identifiers</a></li> -</ul> - -<p> - For more information about how to set up devices or virtual devices with a preview system image - for testing, see <a href="{@docRoot}preview/setup-sdk.html">Set up -the Android N SDK</a>. -</p> - - -<h2 id="runtime-permissions">Testing Permissions</h2> - -<p> - The new <a href="{@docRoot}preview/features/runtime-permissions.html">Permissions</a> model - changes the way that permissions are allocated to your app by the user. Instead of granting all - permissions during the install procedure, your app must ask the user for individual permissions - at runtime. For users this behavior provides more granular control over each app’s activities, as - well as better context for understanding why the app is requesting a specific permission. Users - can grant or revoke the permissions granted to an app individually at any time. This feature of - the preview is most likely to have an impact on your app's behavior and may prevent some of your - app features from working, or they may work in a degraded state. -</p> - -<p class="caution"> - This change affects all apps running on the new platform, even those not targeting the new - platform version. The platform provides a limited compatibility behavior for legacy apps, but you - should begin planning your app’s migration to the new permissions model now, with a goal of - publishing an updated version of your app at the official platform launch. -</p> - - -<h3 id="permission-test-tips">Test tips</h3> - -<p> - Use the following test tips to help you plan and execute testing of your app with the new - permissions behavior. -</p> - -<ul> - <li>Identify your app’s current permissions and the related code paths.</li> - <li>Test user flows across permission-protected services and data.</li> - <li>Test with various combinations of granted/revoked permission.</li> - <li>Use the {@code adb} tool to manage permssions from the command line: - <ul> - <li>List permissions and status by group: - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>Grant or revoke one or more permissions using the following syntax:<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>Analyze your app for services that use permissions.</li> -</ul> - -<h3 id="permission-test-strategy">Test strategy</h3> - -<p> - The permissions change affects the structure and design of your app, as well as - the user experience and flows you provide to users. You should assess your app’s current - permissions use and start planning for the new flows you want to offer. The official release of - the platform provides compatibility behavior, but you should plan on updating your app and not - rely on these behaviors. -</p> - -<p> - Identify the permissions that your app actually needs and uses, and then find the various code - paths that use the permission-protected services. You can do this through a combination of - testing on the new platform and code analysis. In testing, you should focus on opting in to - runtime permissions by changing the app’s {@code targetSdkVersion} to the preview version. For - more information, see <a href="{@docRoot}preview/setup-sdk.html#">Set up -the Android N SDK</a>. -</p> - -<p> - Test with various combinations of permissions revoked and added, to highlight the user flows that - depend on permissions. Where a dependency is not obvious or logical you should consider - refactoring or compartmentalizing that flow to eliminate the dependency or make it clear why the - permission is needed. -</p> - -<p> - For more information on the behavior of runtime permissions, testing, and best practices, see the - <a href="{@docRoot}preview/features/runtime-permissions.html">Permissions</a> developer - preview page. -</p> - - -<h2 id="doze-standby">Testing Doze and App Standby</h2> - -<p> - The power saving features of Doze and App Standby limit the amount of background processing that - your app can perform when a device is in an idle state or while your app is not in focus. The - restrictions the system may impose on apps include limited or no network access, - suspended background tasks, suspended Notifications, ignored wake requests, and alarms. To ensure - that your app behaves properly with these power saving optimizations, you should test your app by - simulating these low power states. -</p> - -<h4 id="doze">Testing your app with Doze</h4> - -<p>To test Doze with your app:</p> - -<ol> -<li>Configure a hardware device or virtual device with an Android N system image.</li> -<li>Connect the device to your development machine and install your app.</li> -<li>Run your app and leave it active.</li> -<li>Simulate the device going into Doze mode by running the following commands: - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>Observe the behavior of your app when the device is re-activated. Make sure it - recovers gracefully when the device exits Doze.</li> -</ol> - - -<h4 id="standby">Testing apps with App Standby</h4> - -<p>To test the App Standby mode with your app:</p> - -<ol> - <li>Configure a hardware device or virtual device with an Android N system image.</li> - <li>Connect the device to your development machine and install your app.</li> - <li>Run your app and leave it active.</li> - <li>Simulate the app going into standby mode by running the following commands: - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>Simulate waking your app using the following command: - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>Observe the behavior of your app when it is woken. Make sure it recovers gracefully - from standby mode. In particular, you should check if your app's Notifications and background - jobs continue to function as expected.</li> -</ol> - -<h2 id="ids">Auto Backup for Apps and Device-Specific Identifiers</h2> - -<p>If your app is persisting any device-specific identifiers, such as Google -Cloud Messaging registration ID, in internal storage, -make sure to follow best practices to exclude the storage -location from auto-backup, as described in <a href="{@docRoot}preview/backup/index.html">Auto -Backup for Apps</a>. </p> diff --git a/docs/html/about/versions/nougat/android-7.0.jd b/docs/html/about/versions/nougat/android-7.0.jd index 87c8d936c9ab..1ca540c9c389 100644 --- a/docs/html/about/versions/nougat/android-7.0.jd +++ b/docs/html/about/versions/nougat/android-7.0.jd @@ -1,6 +1,6 @@ -page.title=Android 7.0 APIs -meta.tags="Android 7.0", "android n", "Nougat" -page.tags="Android 7.0", "Nougat", "android n", "developer preview" +page.title=Android 7.0 for Developers +meta.tags="Nougat", "android n" +page.tags="Android 7.0", "Nougat", "android n" page.image=images/cards/card-n-apis_2x.png @jd:body @@ -52,16 +52,20 @@ page.image=images/cards/card-n-apis_2x.png <p> -Android 7.0 -(<a href="{@docRoot}reference/android/os/Build.VERSION_CODES.html#N">N</a>) -offers new features for users and app developers. This document provides -an introduction to the most notable APIs. Make sure, also, -to check out the <href="{@docRoot}about/versions/nougat/behavior.changes.html"> -Behavior Changes</a> to learn about areas where platform changes -may affect your apps. In addition, the various developer guides -can provide more information about key features. +Android 7.0 Nougat introduces a variety of +new features and capabilities for users and developers. +This document highlights what's new for developers. </p> + +<p>Make sure check out the +<href="{@docRoot}about/versions/nougat/android-7.0-changes.html"> +Android 7.0 behavior changes</a> to learn about areas where platform changes +may affect your apps. </p> +<p>To learn more about +the consumer features of Android 7.0, visit <a +href="http://www.android.com">www.android.com</a>.</p> + <h2 id="multi-window_support">Multi-window Support</h2> diff --git a/docs/html/about/versions/nougat/index.jd b/docs/html/about/versions/nougat/index.jd index aaf7e92d55ab..30a3576158d2 100644 --- a/docs/html/about/versions/nougat/index.jd +++ b/docs/html/about/versions/nougat/index.jd @@ -1,6 +1,6 @@ -page.title=Android N Developer Preview -page.tags="preview","developer" -meta.tags="preview", "android" +page.title=Android 7.0 Nougat +page.tags="androidn","versions" +meta.tags="android n", "nougat", "android 7.0" fullpage=true forcelocalnav=true header.hide=1 @@ -17,44 +17,44 @@ footer.hide=1 }) </script> -<section class="dac-expand dac-hero dac-light" style="background-color:#B2DFDB"> +<section class="dac-expand dac-hero dac-light"> <div class="wrap" style="max-width:1100px;margin-top:0"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <div class="cols dac-hero-content" style="padding-bottom:1em;"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em"> + <div class="col-7of16 col-push-8of16" style="padding-left:2em"> <h1 class="dac-hero-title">Android 7.0 Nougat</h1> <p class="dac-hero-description"> - <strong>Android 7.0 Nougat brings a new crop of sweet features to your Android device.</strong> - <strong>Test your apps</strong> on Nexus and other devices. Support new system + Android 7.0 brings new features for performance, productivity, + and security. <strong>Test your apps</strong> with new system behaviors to <strong>save power and memory</strong>. - Extend your apps with <strong>multi-window UI</strong>, + Take advantage of <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/android-7.0.html"> <span class="dac-sprite dac-auto-chevron"></span> Get started - </a><!--<br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Android N (final SDK) - </a><br>--> + </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <div class="col-7of16 col-pull-6of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> + <a href="{@docRoot}about/versions/nougat/android-7.0.html"> <img class="dac-hero-image" src="{@docRoot}images/home/n-preview-hero.png" srcset="{@docRoot}images/home/n-preview-hero.png 1x, - {@docRoot}images/home/n-preview-hero_2x.png 2x"> + {@docRoot}images/home/n-preview-hero_2x.png 2x" /> + </a> </div> - </div> + </div></a> <div class="dac-section dac-small"> <div class="resource-widget resource-flow-layout col-16" data-query="collection:nougat/landing/resources" data-cardSizes="6x2" data-maxResults="3"></div> - </div> + </div> </div> </section> + <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest"> @@ -62,18 +62,12 @@ footer.hide=1 </a> <ul class="dac-actions"> <li class="dac-action"> - <a class="dac-action-link" href="https://developer.android.com/preview/bug"> + <a class="dac-action-link" href="https://source.android.com/source/report-bugs.html"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Report an issue </a> </li> <li class="dac-action"> - <a class="dac-action-link" href="{@docRoot}preview/support.html"> - <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> - See release notes - </a> - </li> - <li class="dac-action"> <a class="dac-action-link" href="{@docRoot}preview/dev-community"> <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> Join dev community @@ -114,7 +108,6 @@ footer.hide=1 data-initial-results="3"></div> </div></section> - <section class="dac-section dac-gray" id="videos"><div class="wrap"> <h1 class="dac-section-title">Videos</h1> <div class="dac-section-subtitle"> @@ -122,7 +115,7 @@ footer.hide=1 </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/videos/first,type:youtube+tag:androidn" + data-query="collection:nougat/landing/videos/first,type:youtube+tag:androidn" data-sortOrder="-timestamp" data-cardSizes="6x6" data-items-per-page="6" @@ -131,20 +124,17 @@ footer.hide=1 </div> </div></section> - <section class="dac-section dac-light" id="resources"><div class="wrap"> <h1 class="dac-section-title">Resources</h1> <div class="dac-section-subtitle"> - Essential information to help you get your apps ready for Android N. + Essential information to help you get your apps ready for Android Nougat. </div> <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" + data-query="collection:nougat/landing/more" data-cardSizes="6x6" data-items-per-page="6" data-maxResults="15" data-initial-results="6"></div> - </div> -</section> - +</section>
\ No newline at end of file diff --git a/docs/html/distribute/essentials/quality/billions.jd b/docs/html/distribute/essentials/quality/billions.jd index 2e14b37e393a..355b27235353 100644 --- a/docs/html/distribute/essentials/quality/billions.jd +++ b/docs/html/distribute/essentials/quality/billions.jd @@ -220,6 +220,13 @@ page.image=/distribute/images/billions-guidelines.png Android training on <a href="{@docRoot}training/basics/network-ops/managing.html">Managing Network Usage</a>.</li> + <li>On devices powered by Android 7.0 (API level 24) and higher, + users can turn on the + <strong>Data Saver</strong> setting, which helps minimize data usage. Android 7.0 + extends {@link android.net.ConnectivityManager} to detect <strong>Data Saver</strong> + settings. For more information about this feature, see + <a href="/training/basics/network-ops/data-saver.html">Data Saver.</a> + </li> </ul> <h4 id="network-behavior">Detect network changes, then change app behavior</h4> <ul> @@ -257,9 +264,11 @@ page.image=/distribute/images/billions-guidelines.png <code>registerReceiver</code></a> to receive this broadcast. After receiving the broadcast, you should reevaluate the current network state and adjust your UI and network usage appropriately. You should not declare this receiver - in your manifest, as it will no longer function beginning with Android N. - For more details see <a href="{@docRoot}preview/behavior-changes.html"> - Android N behavior changes</a>.</li> + in your manifest, as that feature is unavailable in Android 7.0 (API level 24) + and higher. + For more information about this and other changes in Android 7.0, + see <a href="/about/versions/nougat/android-7.0-changes.html"> + Android 7.0 Changes</a>.</li> </ul> <h3 class="rel-resources clearfloat">Related resources</h3> @@ -490,14 +499,18 @@ page.image=/distribute/images/billions-guidelines.png smaller file sizes than its PNG and JPG counterparts, with at least the same image quality. Even at lossy settings, WebP can produce a nearly identical image. Android has had lossy WebP support since Android 4.0 (API - level 14: Ice Cream Sandwich) and support for lossless / transparent WebP since Android 4.2 (API level 17: Jelly Bean).</li> + level 14: Ice Cream Sandwich) and support for lossless / transparent WebP + since Android 4.2 (API level 17: Jelly Bean).</li> <li>If you have many large images across multiple densities, consider using <a href="{@docRoot}google/play/publishing/multiple-apks.html">Multiple APK support</a> to split your APK by density. This results in builds targeted for specific densities, meaning users with low-density devices won’t have to incur the penalty of unused high-density assets.</li> - <li>A detailed guide on reducing your APK size can be found in <a - class="external-link" href="https://medium.com/@wkalicinski/smallerapk-part-4-multi-apk-through-abi-and-density-splits-477083989006"> + <li>For more information about reducing APK size, see + <a href="/topic/performance/reduce-apk-size.html">Reduce APK Size</a> and + <a href="/studio/build/shrink-code.html">Shrink Your Code and Resources</a>. In addition, you can + find a detailed guide on reducing APK size in this <a class="external-link" + href="https://medium.com/@wkalicinski/smallerapk-part-4-multi-apk-through-abi-and-density-splits-477083989006"> series of Medium posts</a>.</li> </ul> <h4 id="appsize-code">Reduce code size</h4> @@ -607,6 +620,19 @@ requests.</p> <ul> <li>Your app should do minimal activity when in the background and when the device is running on battery power.</li> + <li>Sensors, like GPS, can also significantly drain your battery. For this + reason, we recommend that you use the <a + href="https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi"> + <code>FusedLocationProvider</code></a> API. The + <code>FusedLocationProvider</code> API manages the + underlying location technology and provides a simple API so that you can + specify requirements—like high accuracy or low power—at a high + level. It also optimizes the device's use of battery power by caching + locations and batching requests across apps. For more information about the + ideal ways to request location, see the <a + href="{@docRoot}training/location/retrieve-current.html">Getting the Last + Known Location</a> training guide. + </li> <li><a href="{@docRoot}reference/android/os/PowerManager.WakeLock.html">Wake locks</a> are mechanisms to keep devices on so that they can perform background activities. Avoid using wake locks because they prevent the @@ -623,18 +649,9 @@ requests.</p> network connectivity, device charging state, retries, and backoff. Use <code>GcmNetworkManager</code> to perform non-essential background activity when the device is charging and is connected to an unmetered network.</li> - <li>Sensors, like GPS, can also have a significant drain on the battery. The - recommended way to request location is to use the FusedLocationProvider API. - The <a - href="https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi">FusedLocationProvider</a> API manages the - underlying location technology and provides a simple API so that you can - specify requirements—like high accuracy or low power—at a high - level. It also optimizes the device's use of battery power by caching - locations and batching requests across apps. For more information on the - ideal ways to request location, see the <a - href="{@docRoot}training/location/retrieve-current.html">Getting the Last - Known Location</a> training guide. - </li> + <li>For more information on how network activity can drain the battery, and + how to tackle this issue, see <a + href="/topic/performance/power/network/index.html">Reducing Network Battery Drain</a>. </ul> <h3 id="consumption-benchmark">Benchmark battery usage</h3> <ul> @@ -735,9 +752,12 @@ requests.</p> the device’s CPU and GPU. For more information, see the Android training on <a href="{@docRoot}training/improving-layouts/index.html">Improving Layout Performance</a>. </li> + <li>An efficient view hierarchy can speed up your app without increasing the + app's memory footprint. For more information, see + <a href="/topic/performance/optimizing-view-hierarchies.html">Performance + and View Hierarchies.</a> </ul> -<h4 id="content-firstload">If anticipated start speed is low, use launch screen -on first load</h4> +<h4 id="content-firstload">If anticipated start speed is low, use launch screen on first load</h4> <ul> <li>The launch screen is a user’s first experience of your application. Launching your app while displaying a blank canvas increases its perceived @@ -753,6 +773,9 @@ on first load</h4> <li>For more information on implementing splash screens, see the <a href="https://www.google.com/design/spec/patterns/launch-screens.html"> Launch screens</a> section of the Material Design spec.</li> + <li>The best way to deal with slow start speeds is not to have them. <a + href="/topic/performance/launch-time.html">Launch-Time Performance</a> provides + information that may help you speed up your app's launch time.</li> </ul> <h3 id="ui">UI best practices</h3> <ul> @@ -778,6 +801,12 @@ on first load</h4> <li>To learn more, visit the Android training on <a href="{@docRoot}training/basics/supporting-devices/languages.html"> Supporting Different Languages</a>.</li> + <li>Starting from Android 7.0 (API level 24), the Android framework + makes available a subset of the <a class="external-link" + href="http://userguide.icu-project.org/">ICU4J APIs</a>, which can + help you localize your app into multiple languages. For more + information, see <a href="/guide/topics/resources/icu4j-framework.html"> + ICU4J Android Framework APIs.</a> </ul> <h3 class="rel-resources clearfloat">Related resources</h3> diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd index 13880ec6b796..89cfff93514c 100755 --- a/docs/html/guide/topics/renderscript/compute.jd +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -10,12 +10,13 @@ parent.link=index.html <ol> <li><a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a></li> - <li><a href="#access-rs-apis">Accessing RenderScript APIs</a> + <li><a href="#access-rs-apis">Accessing RenderScript APIs from Java</a> <ol> <li><a href="#ide-setup">Setting Up Your Development Environment</a></li> </ol> </li> <li><a href="#using-rs-from-java">Using RenderScript from Java Code</a></li> + <li><a href="#single-source-rs">Single-Source RenderScript</a></li> <li><a href="#reduction-in-depth">Reduction Kernels in Depth</a> <ol> <li><a href="#writing-reduction-kernel">Writing a reduction kernel</a></li> @@ -45,12 +46,16 @@ computer vision.</p> <p>To begin with RenderScript, there are two main concepts you should understand:</p> <ul> -<li>High-performance compute kernels are written in a C99-derived language. A <i>compute - kernel</i> is a function or collection of functions that you can direct the RenderScript runtime - to execute in parallel across a collection of data.</li> +<li>The <em>language</em> itself is a C99-derived language for writing high-performance compute +code. <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> describes +how to use it to write compute kernels.</li> -<li>A Java API is used for managing the lifetime of RenderScript resources and controlling kernel -execution.</li> +<li>The <em>control API</em> is used for managing the lifetime of RenderScript resources and +controlling kernel execution. It is available in three different languages: Java, C++ in Android +NDK, and the C99-derived kernel language itself. +<a href="#using-rs-from-java">Using RenderScript from Java Code</a> and +<a href=#single-source-rs>Single-Source RenderScript</a> describe the first and the third +options, respectively.</li> </ul> <h2 id="writing-an-rs-kernel">Writing a RenderScript Kernel</h2> @@ -77,7 +82,9 @@ initial setup or serial computations within a larger processing pipeline.</li> access script globals from Java code, and these are often used for parameter passing to RenderScript kernels.</p></li> -<li><p>Zero or more <strong><i>compute kernels</i></strong>. There are two kinds of compute +<li><p>Zero or more <strong><i>compute kernels</i></strong>. A compute kernel is a function +or collection of functions that you can direct the RenderScript runtime to execute in parallel +across a collection of data. There are two kinds of compute kernels: <i>mapping</i> kernels (also called <i>foreach</i> kernels) and <i>reduction</i> kernels.</p> @@ -243,9 +250,9 @@ beneficial on some architectures due to additional optimizations only available precision (such as SIMD CPU instructions).</p> -<h2 id="access-rs-apis">Accessing RenderScript APIs</h2> +<h2 id="access-rs-apis">Accessing RenderScript APIs from Java</h2> -<p>When developing an Android application that uses RenderScript, you can access its API in +<p>When developing an Android application that uses RenderScript, you can access its API from Java in one of two ways:</p> <ul> @@ -377,7 +384,7 @@ to you when using RenderScript: <ul> <li><strong>ScriptC</strong>: These are the user-defined scripts as described in <a -href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> above. Every script has a Java class +href="#writing-an-rs-kernel"><i>Writing a RenderScript Kernel</i></a> above. Every script has a Java class reflected by the RenderScript compiler in order to make it easy to access the script from Java code; this class has the name <code>ScriptC_<i>filename</i></code>. For example, if the mapping kernel above were located in <code>invert.rs</code> and a RenderScript context were already located in @@ -448,6 +455,116 @@ context to throw an exception.</li> </ol> a <code>get()</code> method to obtain the result of a reduction. <code>get()</code> is synchronous, and is serialized with respect to the reduction (which is asynchronous).</p> +<h2 id="single-source-rs">Single-Source RenderScript</h2> + +<p>Android 7.0 (API level 24) introduces a new programming feature called <em>Single-Source +RenderScript</em>, in which kernels are launched from the script where they are defined, rather than +from Java. This approach is currently limited to mapping kernels, which are simply referred to as "kernels" +in this section for conciseness. This new feature also supports creating allocations of type +<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation> +<code>rs_allocation</code></a> from inside the script. It is now possible to +implement a whole algorithm solely within a script, even if multiple kernel launches are required. +The benefit is twofold: more readable code, because it keeps the implementation of an algorithm in +one language; and potentially faster code, because of fewer transitions between Java and +RenderScript across multiple kernel launches.</p> + +<p>In Single-Source RenderScript, you write kernels as described in <a href="#writing-an-rs-kernel"> +Writing a RenderScript Kernel</a>. You then write an invokable function that calls +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEach"> +<code>rsForEach()</code></a> to launch them. That API takes a kernel function as the first +parameter, followed by input and output allocations. A similar API +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rsForEachWithOptions"> +<code>rsForEachWithOptions()</code></a> takes an extra argument of type +<a href="{@docRoot}guide/topics/renderscript/reference/rs_for_each.html#android_rs:rs_script_call_t"> +<code>rs_script_call_t</code></a>, which specifies a subset of the elements from the input and +output allocations for the kernel function to process.</p> + +<p>To start RenderScript computation, you call the invokable function from Java. +Follow the steps in <a href="#using-rs-from-java">Using RenderScript from Java Code</a>. +In the step <a href="#launching_kernels">launch the appropriate kernels</a>, call +the invokable function using <code>invoke_<i>function_name</i>()</code>, which will start the +whole computation, including launching kernels.</p> + +<p>Allocations are often needed to save and pass +intermediate results from one kernel launch to another. You can create them using +<a href="{@docRoot}guide/topics/renderscript/reference/rs_allocation_create.html#android_rs:rsCreateAllocation"> +rsCreateAllocation()</a>. One easy-to-use form of that API is <code> +rsCreateAllocation_<T><W>(…)</code>, where <i>T</i> is the data type for an +element, and <i>W</i> is the vector width for the element. The API takes the sizes in +dimensions X, Y, and Z as arguments. For 1D or 2D allocations, the size for dimension Y or Z can +be omitted. For example, <code>rsCreateAllocation_uchar4(16384)</code> creates a 1D allocation of +16384 elements, each of which is of type <code>uchar4</code>.</p> + +<p>Allocations are managed by the system automatically. You +do not have to explicitly release or free them. However, you can call +<a href="{@docRoot}guide/topics/renderscript/reference/rs_object_info.html#android_rs:rsClearObject"> +<code>rsClearObject(rs_allocation* alloc)</code></a> to indicate you no longer need the handle +<code>alloc</code> to the underlying allocation, +so that the system can free up resources as early as possible.</p> + +<p>The <a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> section contains an example +kernel that inverts an image. The example below expands that to apply more than one effect to an image, +using Single-Source RenderScript. It includes another kernel, <code>greyscale</code>, which turns a +color image into black-and-white. An invokable function <code>process()</code> then applies those two kernels +consecutively to an input image, and produces an output image. Allocations for both the input and +the output are passed in as arguments of type +<a href={@docRoot}guide/topics/renderscript/reference/rs_object_types.html#android_rs:rs_allocation> +<code>rs_allocation</code></a>.</p> + +<pre> +// File: singlesource.rs + +#pragma version(1) +#pragma rs java_package_name(com.android.rssample) + +static const float4 weight = {0.299f, 0.587f, 0.114f, 0.0f}; + +uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) { + uchar4 out = in; + out.r = 255 - in.r; + out.g = 255 - in.g; + out.b = 255 - in.b; + return out; +} + +uchar4 RS_KERNEL greyscale(uchar4 in) { + const float4 inF = rsUnpackColor8888(in); + const float4 outF = (float4){ dot(inF, weight) }; + return rsPackColorTo8888(outF); +} + +void process(rs_allocation inputImage, rs_allocation outputImage) { + const uint32_t imageWidth = rsAllocationGetDimX(inputImage); + const uint32_t imageHeight = rsAllocationGetDimY(inputImage); + rs_allocation tmp = rsCreateAllocation_uchar4(imageWidth, imageHeight); + rsForEach(invert, inputImage, tmp); + rsForEach(greyscale, tmp, outputImage); +} +</pre> + +<p>You can call the <code>process()</code> function from Java as follows:</p> + +<pre> +// File SingleSource.java + +RenderScript RS = RenderScript.create(context); +ScriptC_singlesource script = new ScriptC_singlesource(RS); +Allocation inputAllocation = Allocation.createFromBitmapResource( + RS, getResources(), R.drawable.image); +Allocation outputAllocation = Allocation.createTyped( + RS, inputAllocation.getType(), + Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT); +script.invoke_process(inputAllocation, outputAllocation); +</pre> + +<p>This example shows how an algorithm that involves two kernel launches can be implemented completely +in the RenderScript language itself. Without Single-Source +RenderScript, you would have to launch both kernels from the Java code, separating kernel launches +from kernel definitions and making it harder to understand the whole algorithm. Not only is the +Single-Source RenderScript code easier to read, it also eliminates the transitioning +between Java and the script across kernel launches. Some iterative algorithms may launch kernels +hundreds of times, making the overhead of such transitioning considerable.</p> + <h2 id="reduction-in-depth">Reduction Kernels in Depth</h2> <p><i>Reduction</i> is the process of combining a collection of data into a single diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd index 09dd5f43b03f..373d0a173c91 100644 --- a/docs/html/guide/topics/ui/notifiers/notifications.jd +++ b/docs/html/guide/topics/ui/notifiers/notifications.jd @@ -1,4 +1,6 @@ page.title=Notifications +page.image=images/android-7.0/notifications-card.png +page.tags="notifications", "alerts" @jd:body <div id="qv-wrapper"> diff --git a/docs/html/images/home/nougat_bg.jpg b/docs/html/images/home/nougat_bg.jpg Binary files differnew file mode 100644 index 000000000000..f2a239dee36a --- /dev/null +++ b/docs/html/images/home/nougat_bg.jpg diff --git a/docs/html/images/home/nougat_bg_2x.jpg b/docs/html/images/home/nougat_bg_2x.jpg Binary files differnew file mode 100644 index 000000000000..5c7295e7438e --- /dev/null +++ b/docs/html/images/home/nougat_bg_2x.jpg diff --git a/docs/html/index.jd b/docs/html/index.jd index b1248afa799e..dc59b71935dc 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -16,34 +16,30 @@ nonavpage=true }) </script> -<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> - <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> - <a href="{@docRoot}preview/index.html"> - <h1 class="dac-hero-title">Android N Developer Preview</h1> - <p class="dac-hero-description"> - <strong>Android N final SDK is now available!</strong> - Get ready for the next version of Android! - <strong>Test your apps</strong> on Nexus and other devices. Support new system - behaviors to <strong>save power and memory</strong>. +<section class="dac-expand dac-hero" style="background-color:#b2dfdb;"> + <div class="wrap" style="max-width:1000px;margin-top:0"> + <div class="col-7of16 col-push-8of16"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <h1 class="dac-hero-title" style="color:#004d40">Android 7.0 Nougat!</h1> + <p class="dac-hero-description" style="color:#004d40"> + <strong>Android 7.0 Nougat is here!</strong> + Get your apps ready for the latest version of Android, with new system + behaviors to <strong>save battery and memory</strong>. Extend your apps with <strong>multi-window UI</strong>, <strong>direct reply notifications</strong> and more. </p> - <a class="dac-hero-cta" href="/preview/index.html"> - <span class="dac-sprite dac-auto-chevron"></span> + <a class="dac-hero-cta" href="{@docRoot}about/versions/nougat/index.html" style="color:#004d40"> + <span class="dac-sprite dac-auto-chevron" style="background-color:#b2dfdb"></span> Learn more - </a><!--<br> - <a class="dac-hero-cta" href="/preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Update to Developer Preview (final SDK) - </a><br>--> + </a> + </a> </a> </div> - <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:1.5em;padding-right:1.5em;"> - <a href="{@docRoot}preview/index.html"> - <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" - srcset="/images/home/n-preview-hero.png 1x, - /images/home/n-preview-hero_2x.png 2x"> + <div class="col-6of16 col-pull-6of16 dac-hero-figure" style="padding-left:1em;padding-top:1em;"> + <a href="{@docRoot}about/versions/nougat/index.html"> + <img class="dac-hero-image" src="{@docRoot}images/home/nougat_bg.jpg" + srcset="{@docRoot}images/home/nougat_bg.jpg 1x, + {@docRoot}images/home/nougat_bg_2x.jpg 2x"> </a> </div> </div> diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js index c79ad88524fd..dfc30c35337a 100644 --- a/docs/html/jd_extras_en.js +++ b/docs/html/jd_extras_en.js @@ -5559,13 +5559,13 @@ METADATA['en'].collections = { "nougat/landing/resources": { "title": "", "resources": [ - "about/versions/nougat/api-overview.html", - "about/versions/nougat/behavior-changes.html", - "about/versions/nougat/samples.html", + "about/versions/nougat/android-7.0.html", + "about/versions/nougat/android-7.0-changes.html", + "about/versions/nougat/android-7.0-samples.html", ] }, - "preview/landing/videos/first": { + "nougat/landing/videos/first": { "title": "", "resources": [ "https://www.youtube.com/watch?v=CsulIu3UaUM" @@ -5583,21 +5583,20 @@ METADATA['en'].collections = { "training/articles/memory.html", ] }, - - "preview/landing/more": { - "title": "", - "resources": [ - "preview/features/multi-window.html", - "preview/features/notification-updates.html", - "preview/features/background-optimization.html", - "preview/features/data-saver.html", - "preview/features/direct-boot.html", - "preview/features/icu4j-framework.html", - "preview/features/multilingual-support.html", - "preview/features/scoped-folder-access.html", - "preview/features/security-config.html", - "preview/features/picture-in-picture.html", - "preview/features/tv-recording-api.html" + "nougat/landing/more": { + "title": "", + "resources": [ + "guide/topics/ui/multi-window.html", + "guide/topics/ui/notifiers/notifications.html", + "topic/performance/background-optimization.html", + "training/basics/network-ops/data-saver.html", + "training/articles/direct-boot.html", + "guide/topics/resources/icu4j-framework.html", + "guide/topics/resources/multilingual-support.html", + "training/articles/scoped-directory-access.html", + "training/articles/security-config.html", + "training/tv/playback/picture-in-picture.html", + "training/tv/tif/content-recording.html" ] }, "work/landing/primary": { diff --git a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd index 00622ee95f7d..8fc4dcac25d4 100644 --- a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd +++ b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd @@ -95,6 +95,19 @@ dependencies { } </pre> +<div class="caution"> +<p><strong>Caution:</strong> If your build configuration includes a +<code>compile</code> dependency for the <code>support-annotations</code> +library <b>and</b> an <code>androidTestCompile</code> dependency for the +<code>espresso-core</code> library, your build might fail due to a dependency +conflict. To resolve, update your dependency for <code>espresso-core</code> +as follows:</p> +<pre> +androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' +}) +</pre> +</div> <p> To use JUnit 4 test classes, make sure to specify <a href= diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index c836204486b0..0f305f3cff3d 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -144,6 +144,55 @@ import java.util.ArrayList; * android:valueType="pathType"/> * </set> * </pre></li> + * <p> + * Since AAPT tool is now supporting a new format which can bundle several related XML files into + * one, we can merge the previous example into one XML file, like this: + * </p> + * <pre> + * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" > + * <aapt:attr name="android:drawable"> + * <vector + * android:height="64dp" + * android:width="64dp" + * android:viewportHeight="600" + * android:viewportWidth="600" > + * <group + * android:name="rotationGroup" + * android:pivotX="300.0" + * android:pivotY="300.0" + * android:rotation="45.0" > + * <path + * android:name="v" + * android:fillColor="#000000" + * android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /> + * </group> + * </vector> + * </aapt:attr> + * + * <target android:name="rotationGroup"> * + * <aapt:attr name="android:animation"> + * <objectAnimator + * android:duration="6000" + * android:propertyName="rotation" + * android:valueFrom="0" + * android:valueTo="360" /> + * </aapt:attr> + * </target> + * + * <target android:name="v" > + * <aapt:attr name="android:animation"> + * <set> + * <objectAnimator + * android:duration="3000" + * android:propertyName="pathData" + * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" + * android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z" + * android:valueType="pathType"/> + * </set> + * </aapt:attr> + * </target> + * </animated-vector> + * </pre> * * @attr ref android.R.styleable#AnimatedVectorDrawable_drawable * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_name diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index dc1d18f3b2bd..9ff69650294d 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -27,9 +27,9 @@ import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Insets; import android.graphics.PixelFormat; +import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; -import android.graphics.PorterDuff.Mode; import android.graphics.Shader; import android.util.ArrayMap; import android.util.AttributeSet; @@ -140,12 +140,16 @@ import dalvik.system.VMRuntime; * in the SVG's path data. This is defined in the viewport space.</dd> * <dt><code>android:fillColor</code></dt> * <dd>Specifies the color used to fill the path. May be a color or, for SDK 24+, a color state list - * or a gradient color. If this property is animated, any value set by the animation will - * override the original value. No path fill is drawn if this property is not specified.</dd> + * or a gradient color (See {@link android.R.styleable#GradientColor} + * and {@link android.R.styleable#GradientColorItem}). + * If this property is animated, any value set by the animation will override the original value. + * No path fill is drawn if this property is not specified.</dd> * <dt><code>android:strokeColor</code></dt> * <dd>Specifies the color used to draw the path outline. May be a color or, for SDK 24+, a color - * state list or a gradient color. If this property is animated, any value set by the animation will - * override the original value. No path outline is drawn if this property is not specified.</dd> + * state list or a gradient color (See {@link android.R.styleable#GradientColor} + * and {@link android.R.styleable#GradientColorItem}). + * If this property is animated, any value set by the animation will override the original value. + * No path outline is drawn if this property is not specified.</dd> * <dt><code>android:strokeWidth</code></dt> * <dd>The width a path stroke.</dd> * <dt><code>android:strokeAlpha</code></dt> @@ -166,8 +170,9 @@ import dalvik.system.VMRuntime; * <dt><code>android:strokeMiterLimit</code></dt> * <dd>Sets the Miter limit for a stroked path.</dd> * <dt><code>android:fillType</code></dt> - * <dd>Sets the fillType for a path. It is the same as SVG's "fill-rule" properties. - * For more details, see https://www.w3.org/TR/SVG/painting.html#FillRuleProperty</dd> + * <dd>Sets the fillType for a path. The types can be either "evenOdd" or "nonZero". They behave the + * same as SVG's "fill-rule" properties. For more details, see + * <a href="https://www.w3.org/TR/SVG/painting.html#FillRuleProperty">FillRuleProperty</a></dd> * </dl></dd> * </dl> * @@ -201,7 +206,26 @@ import dalvik.system.VMRuntime; * android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /> * </group> * </vector> - * </pre></li> + * </pre> + * </li> + * <li>And here is an example of linear gradient color, which is supported in SDK 24+. + * See more details in {@link android.R.styleable#GradientColor} and + * {@link android.R.styleable#GradientColorItem}. + * <pre> + * <gradient xmlns:android="http://schemas.android.com/apk/res/android" + * android:angle="90" + * android:startColor="?android:attr/colorPrimary" + * android:endColor="?android:attr/colorControlActivated" + * android:centerColor="#f00" + * android:startX="0" + * android:startY="0" + * android:endX="100" + * android:endY="100" + * android:type="linear"> + * </gradient> + * </pre> + * </li> + * */ public class VectorDrawable extends Drawable { diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index cce58c2096f3..a96ca3922296 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import android.app.Activity; import android.app.PendingIntent; +import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -356,6 +357,9 @@ public final class KeyChain { * * <p> This method may block while waiting for a connection to another process, and must never * be called from the main thread. + * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed + * at any time from the main thread, it is safer to rely on a long-lived context such as one + * returned from {@link Context#getApplicationContext()}. * * @param alias The alias of the desired private key, typically returned via * {@link KeyChainAliasCallback#alias}. @@ -368,7 +372,7 @@ public final class KeyChain { if (alias == null) { throw new NullPointerException("alias == null"); } - KeyChainConnection keyChainConnection = bind(context); + KeyChainConnection keyChainConnection = bind(context.getApplicationContext()); try { final IKeyChainService keyChainService = keyChainConnection.getService(); final String keyId = keyChainService.requestPrivateKey(alias); @@ -400,6 +404,9 @@ public final class KeyChain { * * <p> This method may block while waiting for a connection to another process, and must never * be called from the main thread. + * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed + * at any time from the main thread, it is safer to rely on a long-lived context such as one + * returned from {@link Context#getApplicationContext()}. * * @param alias The alias of the desired certificate chain, typically * returned via {@link KeyChainAliasCallback#alias}. @@ -412,7 +419,7 @@ public final class KeyChain { if (alias == null) { throw new NullPointerException("alias == null"); } - KeyChainConnection keyChainConnection = bind(context); + KeyChainConnection keyChainConnection = bind(context.getApplicationContext()); try { IKeyChainService keyChainService = keyChainConnection.getService(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java index 14d4e2d942d9..02a912734239 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Events.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java @@ -53,7 +53,8 @@ public final class Events { */ public static boolean isTouchType(int toolType) { return toolType == MotionEvent.TOOL_TYPE_FINGER - || toolType == MotionEvent.TOOL_TYPE_STYLUS; + || toolType == MotionEvent.TOOL_TYPE_STYLUS + || toolType == MotionEvent.TOOL_TYPE_UNKNOWN; } /** diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index cce619e67d19..4950af3e14e0 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -873,7 +873,7 @@ class MtpDatabase { } private static int getRootFlags(int[] operationsSupported) { - int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD; + int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY; if (MtpDeviceRecord.isWritingSupported(operationsSupported)) { rootFlag |= Root.FLAG_SUPPORTS_CREATE; } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java index 404047b8baed..8c13c813552b 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java @@ -128,7 +128,7 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.moveToNext(); assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID)); assertEquals( - Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY, getInt(cursor, Root.COLUMN_FLAGS)); assertEquals(R.drawable.ic_root_mtp, getInt(cursor, Root.COLUMN_ICON)); assertEquals("Device Storage", getString(cursor, Root.COLUMN_TITLE)); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index 9ed15c82f336..d19b46083f91 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -210,7 +210,11 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { assertEquals(2, cursor.getCount()); cursor.moveToNext(); assertEquals("1", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | + Root.FLAG_SUPPORTS_CREATE | + Root.FLAG_LOCAL_ONLY, + cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device A Storage A", cursor.getString(3)); assertEquals("1", cursor.getString(4)); @@ -225,7 +229,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); cursor.moveToNext(); assertEquals("2", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY, cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("2", cursor.getString(4)); @@ -271,7 +276,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("1", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY, + cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device A", cursor.getString(3)); assertEquals("1", cursor.getString(4)); @@ -279,7 +286,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("2", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals( + Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY, + cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("2", cursor.getString(4)); diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 7d42211be5e5..682af8bd536f 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -863,4 +863,7 @@ <!-- Label for Help and feedback menu item --> <string name="help_feedback_label">Help & feedback</string> + <!-- Content description for drawer menu button [CHAR_LIMIT=30]--> + <string name="content_description_menu_button">Menu</string> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java index 741b0ea82951..21786c917763 100644 --- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java +++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java @@ -28,7 +28,6 @@ import android.util.Xml; import android.provider.Settings; import android.accounts.Account; import android.accounts.AccountManager; -import android.content.ContentValues; import android.content.pm.PackageManager; import android.content.res.Resources; import android.view.InflateException; @@ -176,6 +175,9 @@ public class SuggestionParser { } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent()); return false; + } catch (Resources.NotFoundException e) { + Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent(), e); + return false; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index 6658c14bd1cd..a50b366c26ac 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -226,6 +226,7 @@ public class SettingsDrawerActivity extends Activity { public void showMenuIcon() { mShowingMenu = true; getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); + getActionBar().setHomeActionContentDescription(R.string.content_description_menu_button); getActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index 284827b57929..aae9cf6de797 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -29,7 +29,6 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.SparseArray; import android.widget.TextView; - import com.android.settingslib.R; public class AccessPointPreference extends Preference { @@ -44,13 +43,14 @@ public class AccessPointPreference extends Preference { private final StateListDrawable mWifiSld; private final int mBadgePadding; private final UserBadgeCache mBadgeCache; - private TextView mTitleView; + private boolean mForSavedNetworks = false; private AccessPoint mAccessPoint; private Drawable mBadge; private int mLevel; private CharSequence mContentDescription; + private int mDefaultIconResId; static final int[] WIFI_CONNECTION_STRENGTH = { R.string.accessibility_wifi_one_bar, @@ -85,6 +85,24 @@ public class AccessPointPreference extends Preference { refresh(); } + public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, + int iconResId, boolean forSavedNetworks) { + super(context); + mBadgeCache = cache; + mAccessPoint = accessPoint; + mForSavedNetworks = forSavedNetworks; + mAccessPoint.setTag(this); + mLevel = -1; + mDefaultIconResId = iconResId; + + mWifiSld = (StateListDrawable) context.getTheme() + .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); + + // Distance from the end of the title at which this AP's user badge should sit. + mBadgePadding = context.getResources() + .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding); + } + public AccessPoint getAccessPoint() { return mAccessPoint; } @@ -112,7 +130,7 @@ public class AccessPointPreference extends Preference { protected void updateIcon(int level, Context context) { if (level == -1) { - setIcon(null); + safeSetDefaultIcon(); } else { if (getIcon() == null) { // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then @@ -124,16 +142,24 @@ public class AccessPointPreference extends Preference { ? STATE_SECURED : STATE_NONE); Drawable drawable = mWifiSld.getCurrent(); - if (!mForSavedNetworks) { + if (!mForSavedNetworks && drawable != null) { setIcon(drawable); - } else { - setIcon(null); + return; } } + safeSetDefaultIcon(); } } } + private void safeSetDefaultIcon() { + if (mDefaultIconResId != 0) { + setIcon(mDefaultIconResId); + } else { + setIcon(null); + } + } + protected void updateBadge(Context context) { WifiConfiguration config = mAccessPoint.getConfig(); if (config != null) { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index bdb103aed7e7..de2511574e09 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -150,9 +150,6 @@ <!-- DevicePolicyManager get user restrictions --> <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> - <!-- Needed for passing extras with intent ACTION_SHOW_ADMIN_SUPPORT_DETAILS --> - <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> - <!-- TV picture-in-picture --> <uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" /> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index d0d6c506f72d..2aad63c085f5 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -32,7 +32,7 @@ <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ծանուցումներ չկան"</string> <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string> - <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցը լիցքաթափվում է"</string> + <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցի լիցքը սպառվում է"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>: Մարտկոցի տնտեսումը միացված է:"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB լիցքավորումը չի աջակցվում:\nՕգտվեք միայն գործող լիցքավորիչից:"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c2897cd77761..8d44048b9582 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -218,12 +218,6 @@ <!-- Doze: should notifications be used as a pulse signal? --> <bool name="doze_pulse_on_notifications">true</bool> - <!-- Doze: when to pulse after a buzzworthy notification arrives --> - <string name="doze_pulse_schedule" translatable="false">10s,30s,60s</string> - - <!-- Doze: maximum number of times the notification pulse schedule can be reset --> - <integer name="doze_pulse_schedule_resets">2</integer> - <!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations --> <integer name="doze_pickup_vibration_threshold">2000</integer> @@ -253,7 +247,7 @@ <integer name="doze_pulse_duration_in_pickup">130</integer> <!-- Doze: pulse parameter - once faded in, how long does it stay visible? --> - <integer name="doze_pulse_duration_visible">3000</integer> + <integer name="doze_pulse_duration_visible">6000</integer> <!-- Doze: pulse parameter - how long does it take to fade out? --> <integer name="doze_pulse_duration_out">600</integer> diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 7c8d0f64c11c..874021ade90c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -33,7 +33,7 @@ public class DozeLog { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean ENABLED = true; private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50; - private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); private static final int PULSE_REASONS = 4; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index fc0d8bb376d0..ec4f447d3a38 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -16,8 +16,6 @@ package com.android.systemui.doze; -import android.app.AlarmManager; -import android.app.PendingIntent; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -43,7 +41,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.SystemUIApplication; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.DozeParameters.PulseSchedule; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -55,19 +52,6 @@ public class DozeService extends DreamService { private static final String ACTION_BASE = "com.android.systemui.doze"; private static final String PULSE_ACTION = ACTION_BASE + ".pulse"; - private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse"; - private static final String EXTRA_INSTANCE = "instance"; - - /** - * Earliest time we pulse due to a notification light after the service started. - * - * <p>Incoming notification light events during the blackout period are - * delayed to the earliest time defined by this constant.</p> - * - * <p>This delay avoids a pulse immediately after screen off, at which - * point the notification light is re-enabled again by NoMan.</p> - */ - private static final int EARLIEST_LIGHT_PULSE_AFTER_START_MS = 10 * 1000; private final String mTag = String.format(TAG + ".%08x", hashCode()); private final Context mContext = this; @@ -80,19 +64,14 @@ public class DozeService extends DreamService { private TriggerSensor mPickupSensor; private PowerManager mPowerManager; private PowerManager.WakeLock mWakeLock; - private AlarmManager mAlarmManager; private UiModeManager mUiModeManager; private boolean mDreaming; private boolean mPulsing; private boolean mBroadcastReceiverRegistered; private boolean mDisplayStateSupported; - private boolean mNotificationLightOn; private boolean mPowerSaveActive; private boolean mCarMode; private long mNotificationPulseTime; - private long mLastScheduleResetTime; - private long mEarliestPulseDueToLight; - private int mScheduleResetsRemaining; public DozeService() { if (DEBUG) Log.d(mTag, "new DozeService()"); @@ -110,11 +89,11 @@ public class DozeService extends DreamService { pw.print(" mSigMotionSensor: "); pw.println(mSigMotionSensor); pw.print(" mPickupSensor:"); pw.println(mPickupSensor); pw.print(" mDisplayStateSupported: "); pw.println(mDisplayStateSupported); - pw.print(" mNotificationLightOn: "); pw.println(mNotificationLightOn); pw.print(" mPowerSaveActive: "); pw.println(mPowerSaveActive); pw.print(" mCarMode: "); pw.println(mCarMode); - pw.print(" mNotificationPulseTime: "); pw.println(mNotificationPulseTime); - pw.print(" mScheduleResetsRemaining: "); pw.println(mScheduleResetsRemaining); + pw.print(" mNotificationPulseTime: "); pw.println( + DozeLog.FORMAT.format(new Date(mNotificationPulseTime + - SystemClock.elapsedRealtime() + System.currentTimeMillis()))); mDozeParameters.dump(pw); } @@ -141,7 +120,6 @@ public class DozeService extends DreamService { mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mWakeLock.setReferenceCounted(true); - mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mDisplayStateSupported = mDozeParameters.getDisplayStateSupported(); mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE); turnDisplayOff(); @@ -176,8 +154,6 @@ public class DozeService extends DreamService { } mDreaming = true; - rescheduleNotificationPulse(false /*predicate*/); // cancel any pending pulse alarms - mEarliestPulseDueToLight = System.currentTimeMillis() + EARLIEST_LIGHT_PULSE_AFTER_START_MS; listenForPulseSignals(true); // Ask the host to get things ready to start dozing. @@ -316,7 +292,6 @@ public class DozeService extends DreamService { private void listenForBroadcasts(boolean listen) { if (listen) { final IntentFilter filter = new IntentFilter(PULSE_ACTION); - filter.addAction(NOTIFICATION_PULSE_ACTION); filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE); mContext.registerReceiver(mBroadcastReceiver, filter); mBroadcastReceiverRegistered = true; @@ -330,93 +305,17 @@ public class DozeService extends DreamService { private void listenForNotifications(boolean listen) { if (listen) { - resetNotificationResets(); mHost.addCallback(mHostCallback); - - // Continue to pulse for existing LEDs. - mNotificationLightOn = mHost.isNotificationLightOn(); - if (mNotificationLightOn) { - updateNotificationPulseDueToLight(); - } } else { mHost.removeCallback(mHostCallback); } } - private void resetNotificationResets() { - if (DEBUG) Log.d(mTag, "resetNotificationResets"); - mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets(); - } - - private void updateNotificationPulseDueToLight() { - long timeMs = System.currentTimeMillis(); - timeMs = Math.max(timeMs, mEarliestPulseDueToLight); - updateNotificationPulse(timeMs); - } - - private void updateNotificationPulse(long notificationTimeMs) { - if (DEBUG) Log.d(mTag, "updateNotificationPulse notificationTimeMs=" + notificationTimeMs); + private void requestNotificationPulse() { + if (DEBUG) Log.d(mTag, "requestNotificationPulse"); if (!mDozeParameters.getPulseOnNotifications()) return; - if (mScheduleResetsRemaining <= 0) { - if (DEBUG) Log.d(mTag, "No more schedule resets remaining"); - return; - } - final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/); - boolean pulseImmediately = System.currentTimeMillis() >= notificationTimeMs; - if ((notificationTimeMs - mLastScheduleResetTime) >= pulseDuration) { - mScheduleResetsRemaining--; - mLastScheduleResetTime = notificationTimeMs; - } else if (!pulseImmediately){ - if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule"); - return; - } - if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining); - mNotificationPulseTime = notificationTimeMs; - if (pulseImmediately) { - DozeLog.traceNotificationPulse(mContext, 0); - requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); - } - // schedule the rest of the pulses - rescheduleNotificationPulse(true /*predicate*/); - } - - private PendingIntent notificationPulseIntent(long instance) { - return PendingIntent.getBroadcast(mContext, 0, - new Intent(NOTIFICATION_PULSE_ACTION) - .setPackage(getPackageName()) - .putExtra(EXTRA_INSTANCE, instance) - .setFlags(Intent.FLAG_RECEIVER_FOREGROUND), - PendingIntent.FLAG_UPDATE_CURRENT); - } - - private void rescheduleNotificationPulse(boolean predicate) { - if (DEBUG) Log.d(mTag, "rescheduleNotificationPulse predicate=" + predicate); - final PendingIntent notificationPulseIntent = notificationPulseIntent(0); - mAlarmManager.cancel(notificationPulseIntent); - if (!predicate) { - if (DEBUG) Log.d(mTag, " don't reschedule: predicate is false"); - return; - } - final PulseSchedule schedule = mDozeParameters.getPulseSchedule(); - if (schedule == null) { - if (DEBUG) Log.d(mTag, " don't reschedule: schedule is null"); - return; - } - final long now = System.currentTimeMillis(); - final long time = schedule.getNextTime(now, mNotificationPulseTime); - if (time <= 0) { - if (DEBUG) Log.d(mTag, " don't reschedule: time is " + time); - return; - } - final long delta = time - now; - if (delta <= 0) { - if (DEBUG) Log.d(mTag, " don't reschedule: delta is " + delta); - return; - } - final long instance = time - mNotificationPulseTime; - if (DEBUG) Log.d(mTag, "Scheduling pulse " + instance + " in " + delta + "ms for " - + new Date(time)); - mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, notificationPulseIntent(instance)); + mNotificationPulseTime = SystemClock.elapsedRealtime(); + requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); } private static String triggerEventToString(TriggerEvent event) { @@ -439,13 +338,6 @@ public class DozeService extends DreamService { if (DEBUG) Log.d(mTag, "Received pulse intent"); requestPulse(DozeLog.PULSE_REASON_INTENT); } - if (NOTIFICATION_PULSE_ACTION.equals(intent.getAction())) { - final long instance = intent.getLongExtra(EXTRA_INSTANCE, -1); - if (DEBUG) Log.d(mTag, "Received notification pulse intent instance=" + instance); - DozeLog.traceNotificationPulse(mContext, instance); - requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); - rescheduleNotificationPulse(mNotificationLightOn); - } if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { mCarMode = true; if (mCarMode && mDreaming) { @@ -465,17 +357,13 @@ public class DozeService extends DreamService { @Override public void onBuzzBeepBlinked() { if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked"); - updateNotificationPulse(System.currentTimeMillis()); + requestNotificationPulse(); } @Override public void onNotificationLight(boolean on) { - if (DEBUG) Log.d(mTag, "onNotificationLight on=" + on); - if (mNotificationLightOn == on) return; - mNotificationLightOn = on; - if (mNotificationLightOn) { - updateNotificationPulseDueToLight(); - } + if (DEBUG) Log.d(mTag, "onNotificationLight (noop) on=" + on); + // noop for now } @Override @@ -564,17 +452,12 @@ public class DozeService extends DreamService { requestPulse(mPulseReason, sensorPerformsProxCheck); updateListener(); // reregister, this sensor only fires once - // reset the notification pulse schedule, but only if we think we were not triggered - // by a notification-related vibration - final long timeSinceNotification = System.currentTimeMillis() + // record pickup gesture, also keep track of whether we might have been triggered + // by recent vibration. + final long timeSinceNotification = SystemClock.elapsedRealtime() - mNotificationPulseTime; final boolean withinVibrationThreshold = timeSinceNotification < mDozeParameters.getPickupVibrationThreshold(); - if (withinVibrationThreshold) { - if (DEBUG) Log.d(mTag, "Not resetting schedule, recent notification"); - } else { - resetNotificationResets(); - } if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { DozeLog.tracePickupPulse(mContext, withinVibrationThreshold); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 02fdd3fc6424..68de16b2af21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -507,7 +507,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { int intrinsicHeight = getIntrinsicHeight(); mIsPinned = pinned; if (intrinsicHeight != getIntrinsicHeight()) { - notifyHeightChanged(false); + notifyHeightChanged(false /* needsAnimation */); } if (pinned) { setIconAnimationRunning(true); @@ -840,8 +840,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } public void resetHeight() { - mMaxExpandHeight = 0; - mHeadsUpHeight = 0; onHeightReset(); requestLayout(); } @@ -1290,7 +1288,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } mHeadsUpHeight = headsUpChild.getHeight(); if (intrinsicBefore != getIntrinsicHeight()) { - notifyHeightChanged(false /* needsAnimation */); + notifyHeightChanged(true /* needsAnimation */); } } @@ -1398,7 +1396,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { if (isChildInGroup()) { mGroupManager.setGroupExpanded(mStatusBarNotification, true); } - notifyHeightChanged(false); + notifyHeightChanged(false /* needsAnimation */); } public void setChildrenExpanded(boolean expanded, boolean animate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 9b3ed33e3158..efceed1d1743 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -38,8 +38,6 @@ public class DozeParameters { private final Context mContext; - private static PulseSchedule sPulseSchedule; - private static IntInOutMatcher sPickupSubtypePerformsProxMatcher; public DozeParameters(Context context) { @@ -61,8 +59,6 @@ public class DozeParameters { pw.print(" getVibrateOnPickup(): "); pw.println(getVibrateOnPickup()); pw.print(" getProxCheckBeforePulse(): "); pw.println(getProxCheckBeforePulse()); pw.print(" getPulseOnNotifications(): "); pw.println(getPulseOnNotifications()); - pw.print(" getPulseSchedule(): "); pw.println(getPulseSchedule()); - pw.print(" getPulseScheduleResets(): "); pw.println(getPulseScheduleResets()); pw.print(" getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); pw.print(" getPickupSubtypePerformsProxCheck(): ");pw.println( dumpPickupSubtypePerformsProxCheck()); @@ -126,18 +122,6 @@ public class DozeParameters { return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications); } - public PulseSchedule getPulseSchedule() { - final String spec = getString("doze.pulse.schedule", R.string.doze_pulse_schedule); - if (sPulseSchedule == null || !sPulseSchedule.mSpec.equals(spec)) { - sPulseSchedule = PulseSchedule.parse(spec); - } - return sPulseSchedule; - } - - public int getPulseScheduleResets() { - return getInt("doze.pulse.schedule.resets", R.integer.doze_pulse_schedule_resets); - } - public int getPickupVibrationThreshold() { return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold); } @@ -249,44 +233,4 @@ public class DozeParameters { return (mIsIn.get(value, mDefaultIsIn)); } } - - public static class PulseSchedule { - private static final Pattern PATTERN = Pattern.compile("(\\d+?)s", 0); - - private String mSpec; - private int[] mSchedule; - - public static PulseSchedule parse(String spec) { - if (TextUtils.isEmpty(spec)) return null; - try { - final PulseSchedule rt = new PulseSchedule(); - rt.mSpec = spec; - final String[] tokens = spec.split(","); - rt.mSchedule = new int[tokens.length]; - for (int i = 0; i < tokens.length; i++) { - final Matcher m = PATTERN.matcher(tokens[i]); - if (!m.matches()) throw new IllegalArgumentException("Bad token: " + tokens[i]); - rt.mSchedule[i] = Integer.parseInt(m.group(1)); - } - if (DEBUG) Log.d(TAG, "Parsed spec [" + spec + "] as: " + rt); - return rt; - } catch (RuntimeException e) { - Log.w(TAG, "Error parsing spec: " + spec, e); - return null; - } - } - - @Override - public String toString() { - return Arrays.toString(mSchedule); - } - - public long getNextTime(long now, long notificationTime) { - for (int i = 0; i < mSchedule.length; i++) { - final long time = notificationTime + mSchedule[i] * 1000; - if (time > now) return time; - } - return 0; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java index f98b9e586a1c..df4566b28d8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java @@ -27,7 +27,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE /** * Controls how light status bar flag applies to the icons. */ -public class LightStatusBarController { +public class LightStatusBarController implements BatteryController.BatteryStateChangeCallback { private final StatusBarIconController mIconController; private final BatteryController mBatteryController; @@ -37,6 +37,7 @@ public class LightStatusBarController { private int mDockedStackVisibility; private boolean mFullscreenLight; private boolean mDockedLight; + private int mLastStatusBarMode; private final Rect mLastFullscreenBounds = new Rect(); private final Rect mLastDockedBounds = new Rect(); @@ -45,6 +46,7 @@ public class LightStatusBarController { BatteryController batteryController) { mIconController = iconController; mBatteryController = batteryController; + batteryController.addStateChangedCallback(this); } public void setFingerprintUnlockController( @@ -73,6 +75,7 @@ public class LightStatusBarController { } mFullscreenStackVisibility = newFullscreen; mDockedStackVisibility = newDocked; + mLastStatusBarMode = statusBarMode; mLastFullscreenBounds.set(fullscreenStackBounds); mLastDockedBounds.set(dockedStackBounds); } @@ -123,4 +126,16 @@ public class LightStatusBarController { mIconController.setIconsDark(true, animateChange()); } } + + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + + } + + @Override + public void onPowerSaveChanged(boolean isPowerSave) { + onSystemUiVisibilityChanged(mFullscreenStackVisibility, mDockedStackVisibility, + 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, true /* sbModeChange*/, + mLastStatusBarMode); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index c8c43101c90f..d1de38c7b9e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -216,7 +216,6 @@ public class NotificationStackScrollLayout extends ViewGroup private float mTopPaddingOverflow; private boolean mDontReportNextOverScroll; private boolean mDontClampNextScroll; - private boolean mRequestViewResizeAnimationOnLayout; private boolean mNeedViewResizeAnimation; private View mExpandedGroupView; private boolean mEverythingNeedsAnimation; @@ -518,10 +517,6 @@ public class NotificationStackScrollLayout extends ViewGroup setMaxLayoutHeight(getHeight()); updateContentHeight(); clampScrollPosition(); - if (mRequestViewResizeAnimationOnLayout) { - requestAnimationOnViewResize(null); - mRequestViewResizeAnimationOnLayout = false; - } requestChildrenUpdate(); updateFirstAndLastBackgroundViews(); } @@ -1966,9 +1961,9 @@ public class NotificationStackScrollLayout extends ViewGroup } private void applyCurrentBackgroundBounds() { - if (!mFadingOut) { - mScrimController.setExcludedBackgroundArea(mCurrentBounds); - } + mScrimController.setExcludedBackgroundArea( + mFadingOut || mParentFadingOut || mAmbientState.isDark() ? null + : mCurrentBounds); invalidate(); } @@ -3104,9 +3099,6 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public void onReset(ExpandableView view) { - if (mIsExpanded && mAnimationsEnabled) { - mRequestViewResizeAnimationOnLayout = true; - } updateAnimationState(view); updateChronometerForChild(view); } @@ -3847,11 +3839,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateFadingState() { - if (mFadingOut || mParentFadingOut || mAmbientState.isDark()) { - mScrimController.setExcludedBackgroundArea(null); - } else { - applyCurrentBackgroundBounds(); - } + applyCurrentBackgroundBounds(); updateSrcDrawing(); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b7461ee79548..079383201a99 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -38,7 +38,6 @@ import static android.net.NetworkPolicyManager.uidRulesToString; import android.annotation.Nullable; import android.app.BroadcastOptions; -import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -135,6 +134,8 @@ import com.android.server.connectivity.Nat464Xlat; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkDiagnostics; import com.android.server.connectivity.NetworkMonitor; +import com.android.server.connectivity.NetworkNotificationManager; +import com.android.server.connectivity.NetworkNotificationManager.NotificationType; import com.android.server.connectivity.PacManager; import com.android.server.connectivity.PermissionMonitor; import com.android.server.connectivity.Tethering; @@ -436,6 +437,7 @@ public class ConnectivityService extends IConnectivityManager.Stub TelephonyManager mTelephonyManager; private KeepaliveTracker mKeepaliveTracker; + private NetworkNotificationManager mNotifier; // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp private final static int MIN_NET_ID = 100; // some reserved marks @@ -832,6 +834,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mKeepaliveTracker = new KeepaliveTracker(mHandler); + mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager, + mContext.getSystemService(NotificationManager.class)); } private NetworkRequest createInternetRequestForTransport(int transportType) { @@ -2230,16 +2234,15 @@ public class ConnectivityService extends IConnectivityManager.Stub updateCapabilities(nai, nai.networkCapabilities); } if (!visible) { - setProvNotificationVisibleIntent(false, netId, null, 0, null, null, false); + mNotifier.clearNotification(netId); } else { if (nai == null) { loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor"); break; } if (!nai.networkMisc.provisioningNotificationDisabled) { - setProvNotificationVisibleIntent(true, netId, NotificationType.SIGN_IN, - nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(), - (PendingIntent)msg.obj, nai.networkMisc.explicitlySelected); + mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, + (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected); } } break; @@ -2710,8 +2713,8 @@ public class ConnectivityService extends IConnectivityManager.Stub PendingIntent pendingIntent = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); - setProvNotificationVisibleIntent(true, nai.network.netId, NotificationType.NO_INTERNET, - nai.networkInfo.getType(), nai.networkInfo.getExtraInfo(), pendingIntent, true); + mNotifier.showNotification(nai.network.netId, NotificationType.NO_INTERNET, nai, + pendingIntent, true); } private class InternalHandler extends Handler { @@ -3617,118 +3620,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return -1; } - private static final String NOTIFICATION_ID = "CaptivePortal.Notification"; - private static enum NotificationType { SIGN_IN, NO_INTERNET; }; - - private void setProvNotificationVisible(boolean visible, int networkType, String action) { - Intent intent = new Intent(action); - PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); - // Concatenate the range of types onto the range of NetIDs. - int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE); - setProvNotificationVisibleIntent(visible, id, NotificationType.SIGN_IN, - networkType, null, pendingIntent, false); - } - - /** - * Show or hide network provisioning notifications. - * - * We use notifications for two purposes: to notify that a network requires sign in - * (NotificationType.SIGN_IN), or to notify that a network does not have Internet access - * (NotificationType.NO_INTERNET). We display at most one notification per ID, so on a - * particular network we can display the notification type that was most recently requested. - * So for example if a captive portal fails to reply within a few seconds of connecting, we - * might first display NO_INTERNET, and then when the captive portal check completes, display - * SIGN_IN. - * - * @param id an identifier that uniquely identifies this notification. This must match - * between show and hide calls. We use the NetID value but for legacy callers - * we concatenate the range of types with the range of NetIDs. - */ - private void setProvNotificationVisibleIntent(boolean visible, int id, - NotificationType notifyType, int networkType, String extraInfo, PendingIntent intent, - boolean highPriority) { - if (VDBG || (DBG && visible)) { - log("setProvNotificationVisibleIntent " + notifyType + " visible=" + visible - + " networkType=" + getNetworkTypeName(networkType) - + " extraInfo=" + extraInfo + " highPriority=" + highPriority); - } - - Resources r = Resources.getSystem(); - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - - if (visible) { - CharSequence title; - CharSequence details; - int icon; - if (notifyType == NotificationType.NO_INTERNET && - networkType == ConnectivityManager.TYPE_WIFI) { - title = r.getString(R.string.wifi_no_internet, 0); - details = r.getString(R.string.wifi_no_internet_detailed); - icon = R.drawable.stat_notify_wifi_in_range; // TODO: Need new icon. - } else if (notifyType == NotificationType.SIGN_IN) { - switch (networkType) { - case ConnectivityManager.TYPE_WIFI: - title = r.getString(R.string.wifi_available_sign_in, 0); - details = r.getString(R.string.network_available_sign_in_detailed, - extraInfo); - icon = R.drawable.stat_notify_wifi_in_range; - break; - case ConnectivityManager.TYPE_MOBILE: - case ConnectivityManager.TYPE_MOBILE_HIPRI: - title = r.getString(R.string.network_available_sign_in, 0); - // TODO: Change this to pull from NetworkInfo once a printable - // name has been added to it - details = mTelephonyManager.getNetworkOperatorName(); - icon = R.drawable.stat_notify_rssi_in_range; - break; - default: - title = r.getString(R.string.network_available_sign_in, 0); - details = r.getString(R.string.network_available_sign_in_detailed, - extraInfo); - icon = R.drawable.stat_notify_rssi_in_range; - break; - } - } else { - Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network type " - + getNetworkTypeName(networkType)); - return; - } - - Notification notification = new Notification.Builder(mContext) - .setWhen(0) - .setSmallIcon(icon) - .setAutoCancel(true) - .setTicker(title) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setContentText(details) - .setContentIntent(intent) - .setLocalOnly(true) - .setPriority(highPriority ? - Notification.PRIORITY_HIGH : - Notification.PRIORITY_DEFAULT) - .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0) - .setOnlyAlertOnce(true) - .build(); - - try { - notificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL); - } catch (NullPointerException npe) { - loge("setNotificationVisible: visible notificationManager npe=" + npe); - npe.printStackTrace(); - } - } else { - try { - notificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL); - } catch (NullPointerException npe) { - loge("setNotificationVisible: cancel notificationManager npe=" + npe); - npe.printStackTrace(); - } - } - } - /** Location to an updatable file listing carrier provisioning urls. * An example: * @@ -3832,7 +3723,9 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceConnectivityInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - setProvNotificationVisible(visible, networkType, action); + // Concatenate the range of types onto the range of NetIDs. + int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE); + mNotifier.setProvNotificationVisible(visible, id, action); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 9108acf9c40a..5055562ed9b0 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -22,6 +22,7 @@ import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountManager; +import android.accounts.AccountManagerInternal; import android.accounts.AuthenticatorDescription; import android.accounts.CantAddAccountActivity; import android.accounts.GrantCredentialsPermissionActivity; @@ -29,11 +30,14 @@ import android.accounts.IAccountAuthenticator; import android.accounts.IAccountAuthenticatorResponse; import android.accounts.IAccountManager; import android.accounts.IAccountManagerResponse; +import android.annotation.IntRange; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -46,9 +50,11 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -72,11 +78,14 @@ import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.Process; +import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; +import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -86,6 +95,7 @@ import android.util.SparseBooleanArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -237,6 +247,13 @@ public class AccountManagerService + " AND " + ACCOUNTS_NAME + "=?" + " AND " + ACCOUNTS_TYPE + "=?"; + private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = "" + + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS + + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID + + " AND " + GRANTS_GRANTEE_UID + "=?" + + " AND " + ACCOUNTS_NAME + "=?" + + " AND " + ACCOUNTS_TYPE + "=?"; + private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; @@ -376,6 +393,118 @@ public class AccountManagerService } } }, UserHandle.ALL, userFilter, null, null); + + LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl()); + + // Need to cancel account request notifications if the update/install can access the account + new PackageMonitor() { + @Override + public void onPackageAdded(String packageName, int uid) { + // Called on a handler, and running as the system + cancelAccountAccessRequestNotificationIfNeeded(uid, true); + } + + @Override + public void onPackageUpdateFinished(String packageName, int uid) { + // Called on a handler, and running as the system + cancelAccountAccessRequestNotificationIfNeeded(uid, true); + } + }.register(mContext, mMessageHandler.getLooper(), UserHandle.ALL, true); + + // Cancel account request notification if an app op was preventing the account access + mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null, + new AppOpsManager.OnOpChangedInternalListener() { + @Override + public void onOpChanged(int op, String packageName) { + try { + final int userId = ActivityManager.getCurrentUser(); + final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); + final int mode = mAppOpsManager.checkOpNoThrow( + AppOpsManager.OP_GET_ACCOUNTS, uid, packageName); + if (mode == AppOpsManager.MODE_ALLOWED) { + final long identity = Binder.clearCallingIdentity(); + try { + cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } catch (NameNotFoundException e) { + /* ignore */ + } + } + }); + + // Cancel account request notification if a permission was preventing the account access + mPackageManager.addOnPermissionsChangeListener( + (int uid) -> { + Account[] accounts = null; + String[] packageNames = mPackageManager.getPackagesForUid(uid); + if (packageNames != null) { + final int userId = UserHandle.getUserId(uid); + final long identity = Binder.clearCallingIdentity(); + try { + for (String packageName : packageNames) { + if (mContext.getPackageManager().checkPermission( + Manifest.permission.GET_ACCOUNTS, packageName) + != PackageManager.PERMISSION_GRANTED) { + continue; + } + + if (accounts == null) { + accounts = getAccountsAsUser(null, userId, "android"); + if (ArrayUtils.isEmpty(accounts)) { + return; + } + } + + for (Account account : accounts) { + cancelAccountAccessRequestNotificationIfNeeded( + account, uid, packageName, true); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }); + } + + private void cancelAccountAccessRequestNotificationIfNeeded(int uid, + boolean checkAccess) { + Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android"); + for (Account account : accounts) { + cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess); + } + } + + private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid, + boolean checkAccess) { + Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android"); + for (Account account : accounts) { + cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess); + } + } + + private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid, + boolean checkAccess) { + String[] packageNames = mPackageManager.getPackagesForUid(uid); + if (packageNames != null) { + for (String packageName : packageNames) { + cancelAccountAccessRequestNotificationIfNeeded(account, uid, + packageName, checkAccess); + } + } + } + + private void cancelAccountAccessRequestNotificationIfNeeded(Account account, + int uid, String packageName, boolean checkAccess) { + if (!checkAccess || hasAccountAccess(account, packageName, + UserHandle.getUserHandleForUid(uid))) { + cancelNotification(getCredentialPermissionNotificationId(account, + AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, + UserHandle.getUserHandleForUid(uid)); + } } @Override @@ -1723,6 +1852,21 @@ public class AccountManagerService } finally { Binder.restoreCallingIdentity(id); } + + if (isChanged) { + synchronized (accounts.credentialsPermissionNotificationIds) { + for (Pair<Pair<Account, String>, Integer> key + : accounts.credentialsPermissionNotificationIds.keySet()) { + if (account.equals(key.first.first) + && AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) { + final int uid = (Integer) key.second; + mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded( + account, uid, false)); + } + } + } + } + return isChanged; } @@ -2319,9 +2463,11 @@ public class AccountManagerService if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { Intent intent = newGrantCredentialsPermissionIntent( account, + null, callerUid, new AccountAuthenticatorResponse(this), - authTokenType); + authTokenType, + true); Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); onResult(bundle); @@ -2372,7 +2518,7 @@ public class AccountManagerService intent); doNotification(mAccounts, account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), - intent, accounts.userId); + intent, "android", accounts.userId); } } super.onResult(result); @@ -2403,7 +2549,7 @@ public class AccountManagerService } private void createNoCredentialsPermissionNotification(Account account, Intent intent, - int userId) { + String packageName, int userId) { int uid = intent.getIntExtra( GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); String authTokenType = intent.getStringExtra( @@ -2431,20 +2577,23 @@ public class AccountManagerService PendingIntent.FLAG_CANCEL_CURRENT, null, user)) .build(); installNotification(getCredentialPermissionNotificationId( - account, authTokenType, uid), n, user); + account, authTokenType, uid), n, packageName, user.getIdentifier()); } - private Intent newGrantCredentialsPermissionIntent(Account account, int uid, - AccountAuthenticatorResponse response, String authTokenType) { + private Intent newGrantCredentialsPermissionIntent(Account account, String packageName, + int uid, AccountAuthenticatorResponse response, String authTokenType, + boolean startInNewTask) { Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); - // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. - // Since it was set in Eclair+ we can't change it without breaking apps using - // the intent from a non-Activity context. - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory( - String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid))); + if (startInNewTask) { + // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. + // Since it was set in Eclair+ we can't change it without breaking apps using + // the intent from a non-Activity context. This is the default behavior. + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account, + authTokenType, uid) + (packageName != null ? packageName : ""))); intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); @@ -3295,6 +3444,118 @@ public class AccountManagerService } @Override + public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName, + @NonNull UserHandle userHandle) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Can be called only by system UID"); + } + Preconditions.checkNotNull(account, "account cannot be null"); + Preconditions.checkNotNull(packageName, "packageName cannot be null"); + Preconditions.checkNotNull(userHandle, "userHandle cannot be null"); + + final int userId = userHandle.getIdentifier(); + + Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); + + try { + + final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); + // Use null token which means any token. Having a token means the package + // is trusted by the authenticator, hence it is fine to access the account. + if (permissionIsGranted(account, null, uid, userId)) { + return true; + } + // In addition to the permissions required to get an auth token we also allow + // the account to be accessed by holders of the get accounts permissions. + return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName) + || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName); + } catch (NameNotFoundException e) { + return false; + } + } + + private boolean checkUidPermission(String permission, int uid, String opPackageName) { + final long identity = Binder.clearCallingIdentity(); + try { + IPackageManager pm = ActivityThread.getPackageManager(); + if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) { + return false; + } + final int opCode = AppOpsManager.permissionToOpCode(permission); + return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow( + opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED); + } catch (RemoteException e) { + /* ignore - local call */ + } finally { + Binder.restoreCallingIdentity(identity); + } + return false; + } + + @Override + public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account, + @NonNull String packageName, @NonNull UserHandle userHandle) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Can be called only by system UID"); + } + + Preconditions.checkNotNull(account, "account cannot be null"); + Preconditions.checkNotNull(packageName, "packageName cannot be null"); + Preconditions.checkNotNull(userHandle, "userHandle cannot be null"); + + final int userId = userHandle.getIdentifier(); + + Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); + + final int uid; + try { + uid = mPackageManager.getPackageUidAsUser(packageName, userId); + } catch (NameNotFoundException e) { + Slog.e(TAG, "Unknown package " + packageName); + return null; + } + + Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null); + + return PendingIntent.getActivityAsUser( + mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + null, new UserHandle(userId)).getIntentSender(); + } + + private Intent newRequestAccountAccessIntent(Account account, String packageName, + int uid, RemoteCallback callback) { + return newGrantCredentialsPermissionIntent(account, packageName, uid, + new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() { + @Override + public void onResult(Bundle value) throws RemoteException { + handleAuthenticatorResponse(true); + } + + @Override + public void onRequestContinued() { + /* ignore */ + } + + @Override + public void onError(int errorCode, String errorMessage) throws RemoteException { + handleAuthenticatorResponse(false); + } + + private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { + cancelNotification(getCredentialPermissionNotificationId(account, + AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, + UserHandle.getUserHandleForUid(uid)); + if (callback != null) { + Bundle result = new Bundle(); + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted); + callback.sendResult(result); + } + } + }), AccountManager.ACCOUNT_ACCESS_TOKEN, false); + } + + @Override public boolean someUserHasAccount(@NonNull final Account account) { if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) { throw new SecurityException("Only system can check for accounts across users"); @@ -4934,7 +5195,7 @@ public class AccountManagerService } private void doNotification(UserAccounts accounts, Account account, CharSequence message, - Intent intent, int userId) { + Intent intent, String packageName, final int userId) { long identityToken = clearCallingIdentity(); try { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -4944,12 +5205,12 @@ public class AccountManagerService if (intent.getComponent() != null && GrantCredentialsPermissionActivity.class.getName().equals( intent.getComponent().getClassName())) { - createNoCredentialsPermissionNotification(account, intent, userId); + createNoCredentialsPermissionNotification(account, intent, packageName, userId); } else { + Context contextForUser = getContextForUser(new UserHandle(userId)); final Integer notificationId = getSigninRequiredNotificationId(accounts, account); intent.addCategory(String.valueOf(notificationId)); - UserHandle user = new UserHandle(userId); - Context contextForUser = getContextForUser(user); + final String notificationTitleFormat = contextForUser.getText(R.string.notification_title).toString(); Notification n = new Notification.Builder(contextForUser) @@ -4961,9 +5222,9 @@ public class AccountManagerService .setContentText(message) .setContentIntent(PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, - null, user)) + null, new UserHandle(userId))) .build(); - installNotification(notificationId, n, user); + installNotification(notificationId, n, packageName, userId); } } finally { restoreCallingIdentity(identityToken); @@ -4971,18 +5232,40 @@ public class AccountManagerService } @VisibleForTesting - protected void installNotification(final int notificationId, final Notification n, + protected void installNotification(int notificationId, final Notification notification, UserHandle user) { - ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .notifyAsUser(null, notificationId, n, user); + installNotification(notificationId, notification, "android", user.getIdentifier()); + } + + private void installNotification(int notificationId, final Notification notification, + String packageName, int userId) { + final long token = clearCallingIdentity(); + try { + INotificationManager notificationManager = NotificationManager.getService(); + try { + notificationManager.enqueueNotificationWithTag(packageName, packageName, null, + notificationId, notification, new int[1], userId); + } catch (RemoteException e) { + /* ignore - local call */ + } + } finally { + Binder.restoreCallingIdentity(token); + } } @VisibleForTesting protected void cancelNotification(int id, UserHandle user) { + cancelNotification(id, mContext.getPackageName(), user); + } + + protected void cancelNotification(int id, String packageName, UserHandle user) { long identityToken = clearCallingIdentity(); try { - ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .cancelAsUser(null, id, user); + INotificationManager service = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier()); + } catch (RemoteException e) { + /* ignore - local call */ } finally { restoreCallingIdentity(identityToken); } @@ -5043,18 +5326,40 @@ public class AccountManagerService private boolean permissionIsGranted( Account account, String authTokenType, int callerUid, int userId) { - final boolean isPrivileged = isPrivileged(callerUid); - final boolean fromAuthenticator = account != null - && isAccountManagedByCaller(account.type, callerUid, userId); - final boolean hasExplicitGrants = account != null - && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); + if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Access to " + account + " granted calling uid is system"); + } + return true; + } + + if (isPrivileged(callerUid)) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Access to " + account + " granted calling uid " + + callerUid + " privileged"); + } + return true; + } + if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Access to " + account + " granted calling uid " + + callerUid + " manages the account"); + } + return true; + } + if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Access to " + account + " granted calling uid " + + callerUid + " user granted access"); + } + return true; + } + if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " - + callerUid + ", " + account - + ": is authenticator? " + fromAuthenticator - + ", has explicit permission? " + hasExplicitGrants); + Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid); } - return fromAuthenticator || hasExplicitGrants || isPrivileged; + + return false; } private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, @@ -5144,10 +5449,20 @@ public class AccountManagerService UserAccounts accounts = getUserAccountsForCaller(); synchronized (accounts.cacheLock) { final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - String[] args = { String.valueOf(callerUid), authTokenType, - account.name, account.type}; - final boolean permissionGranted = - DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; + + final String query; + final String[] args; + + if (authTokenType != null) { + query = COUNT_OF_MATCHING_GRANTS; + args = new String[] {String.valueOf(callerUid), authTokenType, + account.name, account.type}; + } else { + query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN; + args = new String[] {String.valueOf(callerUid), account.name, + account.type}; + } + final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0; if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { // TODO: Skip this check when running automated tests. Replace this // with a more general solution. @@ -5288,6 +5603,8 @@ public class AccountManagerService } cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), UserHandle.of(accounts.userId)); + + cancelAccountAccessRequestNotificationIfNeeded(account, uid, true); } } @@ -5605,4 +5922,45 @@ public class AccountManagerService } } } + + private final class AccountManagerInternalImpl extends AccountManagerInternal { + @Override + public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, + @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) { + if (account == null) { + Slog.w(TAG, "account cannot be null"); + return; + } + if (packageName == null) { + Slog.w(TAG, "packageName cannot be null"); + return; + } + if (userId < UserHandle.USER_SYSTEM) { + Slog.w(TAG, "user id must be concrete"); + return; + } + if (callback == null) { + Slog.w(TAG, "callback cannot be null"); + return; + } + + if (hasAccountAccess(account, packageName, new UserHandle(userId))) { + Bundle result = new Bundle(); + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); + callback.sendResult(result); + return; + } + + final int uid; + try { + uid = mPackageManager.getPackageUidAsUser(packageName, userId); + } catch (NameNotFoundException e) { + Slog.e(TAG, "Unknown package " + packageName); + return; + } + + Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback); + doNotification(mUsers.get(userId), account, null, intent, packageName, userId); + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ff3f159b1d08..21ff50ca078b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17777,6 +17777,7 @@ public final class ActivityManagerService extends ActivityManagerNative || Intent.ACTION_MEDIA_BUTTON.equals(action) || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action) || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action) + || Intent.ACTION_MASTER_CLEAR.equals(action) || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action) || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action) diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 7eff773543c8..576f2b27f111 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -575,6 +575,8 @@ class AppErrors { boolean handleAppCrashLocked(ProcessRecord app, String reason, String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { long now = SystemClock.uptimeMillis(); + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; Long crashTime; Long crashTimePersistent; @@ -612,7 +614,9 @@ class AppErrors { // processes run critical code. mService.removeProcessLocked(app, false, false, "crash"); mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); - return false; + if (!showBackground) { + return false; + } } mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); } else { @@ -705,7 +709,7 @@ class AppErrors { } final boolean crashSilenced = mAppsNotReportingCrashes != null && mAppsNotReportingCrashes.contains(proc.info.packageName); - if (mService.canShowErrorDialogs() && !crashSilenced) { + if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced) { proc.crashDialog = new AppErrorDialog(mContext, mService, data); } else { // The device is asleep, so just pretend that the user @@ -942,7 +946,9 @@ class AppErrors { null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); - if (mService.canShowErrorDialogs()) { + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; + if (mService.canShowErrorDialogs() || showBackground) { d = new AppNotRespondingDialog(mService, mContext, proc, (ActivityRecord)data.get("activity"), msg.arg1 != 0); diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 2467a9094072..9c0845363860 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -38,6 +38,7 @@ import com.android.server.am.ActivityStackSupervisor.ActivityContainer; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.util.Objects; final class PendingIntentRecord extends IIntentSender.Stub { private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM; @@ -102,7 +103,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { if (requestResolvedType != null) { hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode(); } - hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode(); + hash = (ODD_PRIME_NUMBER*hash) + (_p != null ? _p.hashCode() : 0); hash = (ODD_PRIME_NUMBER*hash) + _t; hashCode = hash; //Slog.i(ActivityManagerService.TAG, this + " hashCode=0x" @@ -121,20 +122,14 @@ final class PendingIntentRecord extends IIntentSender.Stub { if (userId != other.userId){ return false; } - if (!packageName.equals(other.packageName)) { + if (!Objects.equals(packageName, other.packageName)) { return false; } if (activity != other.activity) { return false; } - if (who != other.who) { - if (who != null) { - if (!who.equals(other.who)) { - return false; - } - } else if (other.who != null) { - return false; - } + if (!Objects.equals(who, other.who)) { + return false; } if (requestCode != other.requestCode) { return false; @@ -148,14 +143,8 @@ final class PendingIntentRecord extends IIntentSender.Stub { return false; } } - if (requestResolvedType != other.requestResolvedType) { - if (requestResolvedType != null) { - if (!requestResolvedType.equals(other.requestResolvedType)) { - return false; - } - } else if (other.requestResolvedType != null) { - return false; - } + if (!Objects.equals(requestResolvedType, other.requestResolvedType)) { + return false; } if (flags != other.flags) { return false; diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java new file mode 100644 index 000000000000..4680a8a1051b --- /dev/null +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.NetworkCapabilities; +import android.os.UserHandle; +import android.telephony.TelephonyManager; +import android.util.Slog; + +import com.android.internal.R; + +import static android.net.NetworkCapabilities.*; + + +public class NetworkNotificationManager { + + public static enum NotificationType { SIGN_IN, NO_INTERNET; }; + + private static final String NOTIFICATION_ID = "CaptivePortal.Notification"; + + private static final String TAG = NetworkNotificationManager.class.getSimpleName(); + private static final boolean DBG = true; + private static final boolean VDBG = false; + + private final Context mContext; + private final TelephonyManager mTelephonyManager; + private final NotificationManager mNotificationManager; + + public NetworkNotificationManager(Context c, TelephonyManager t, NotificationManager n) { + mContext = c; + mTelephonyManager = t; + mNotificationManager = n; + } + + // TODO: deal more gracefully with multi-transport networks. + private static int getFirstTransportType(NetworkAgentInfo nai) { + for (int i = 0; i < 64; i++) { + if (nai.networkCapabilities.hasTransport(i)) return i; + } + return -1; + } + + private static String getTransportName(int transportType) { + Resources r = Resources.getSystem(); + String[] networkTypes = r.getStringArray(R.array.network_switch_type_name); + try { + return networkTypes[transportType]; + } catch (IndexOutOfBoundsException e) { + return r.getString(R.string.network_switch_type_name_unknown); + } + } + + private static int getIcon(int transportType) { + return (transportType == TRANSPORT_WIFI) ? + R.drawable.stat_notify_wifi_in_range : // TODO: Distinguish ! from ?. + R.drawable.stat_notify_rssi_in_range; + } + + /** + * Show or hide network provisioning notifications. + * + * We use notifications for two purposes: to notify that a network requires sign in + * (NotificationType.SIGN_IN), or to notify that a network does not have Internet access + * (NotificationType.NO_INTERNET). We display at most one notification per ID, so on a + * particular network we can display the notification type that was most recently requested. + * So for example if a captive portal fails to reply within a few seconds of connecting, we + * might first display NO_INTERNET, and then when the captive portal check completes, display + * SIGN_IN. + * + * @param id an identifier that uniquely identifies this notification. This must match + * between show and hide calls. We use the NetID value but for legacy callers + * we concatenate the range of types with the range of NetIDs. + */ + public void showNotification(int id, NotificationType notifyType, + NetworkAgentInfo nai, PendingIntent intent, boolean highPriority) { + int transportType; + String extraInfo; + if (nai != null) { + transportType = getFirstTransportType(nai); + extraInfo = nai.networkInfo.getExtraInfo(); + // Only notify for Internet-capable networks. + if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return; + } else { + // Legacy notifications. + transportType = TRANSPORT_CELLULAR; + extraInfo = null; + } + + if (DBG) { + Slog.d(TAG, "showNotification " + notifyType + + " transportType=" + getTransportName(transportType) + + " extraInfo=" + extraInfo + " highPriority=" + highPriority); + } + + Resources r = Resources.getSystem(); + CharSequence title; + CharSequence details; + int icon = getIcon(transportType); + if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) { + title = r.getString(R.string.wifi_no_internet, 0); + details = r.getString(R.string.wifi_no_internet_detailed); + } else if (notifyType == NotificationType.SIGN_IN) { + switch (transportType) { + case TRANSPORT_WIFI: + title = r.getString(R.string.wifi_available_sign_in, 0); + details = r.getString(R.string.network_available_sign_in_detailed, extraInfo); + break; + case TRANSPORT_CELLULAR: + title = r.getString(R.string.network_available_sign_in, 0); + // TODO: Change this to pull from NetworkInfo once a printable + // name has been added to it + details = mTelephonyManager.getNetworkOperatorName(); + break; + default: + title = r.getString(R.string.network_available_sign_in, 0); + details = r.getString(R.string.network_available_sign_in_detailed, extraInfo); + break; + } + } else { + Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport " + + getTransportName(transportType)); + return; + } + + Notification notification = new Notification.Builder(mContext) + .setWhen(0) + .setSmallIcon(icon) + .setAutoCancel(true) + .setTicker(title) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(details) + .setContentIntent(intent) + .setLocalOnly(true) + .setPriority(highPriority ? + Notification.PRIORITY_HIGH : + Notification.PRIORITY_DEFAULT) + .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0) + .setOnlyAlertOnce(true) + .build(); + + try { + mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL); + } catch (NullPointerException npe) { + Slog.d(TAG, "setNotificationVisible: visible notificationManager npe=" + npe); + } + } + + public void clearNotification(int id) { + if (DBG) { + Slog.d(TAG, "clearNotification id=" + id); + } + try { + mNotificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL); + } catch (NullPointerException npe) { + Slog.d(TAG, "setNotificationVisible: cancel notificationManager npe=" + npe); + } + } + + /** + * Legacy provisioning notifications coming directly from DcTracker. + */ + public void setProvNotificationVisible(boolean visible, int id, String action) { + if (visible) { + Intent intent = new Intent(action); + PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); + showNotification(id, NotificationType.SIGN_IN, null, pendingIntent, false); + } else { + clearNotification(id); + } + } +} diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 01b23939815c..12955f5771d2 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -482,7 +482,6 @@ public final class ContentService extends IContentService.Stub { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleSync(account, userId, uId, authority, extras, - 0 /* no delay */, 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */); } } finally { @@ -547,11 +546,8 @@ public final class ContentService extends IContentService.Stub { getSyncManager().updateOrAddPeriodicSync(info, runAtTime, flextime, extras); } else { - long beforeRuntimeMillis = (flextime) * 1000; - long runtimeMillis = runAtTime * 1000; syncManager.scheduleSync( request.getAccount(), userId, callerUid, request.getProvider(), extras, - beforeRuntimeMillis, runtimeMillis, false /* onlyThoseWithUnknownSyncableState */); } } finally { @@ -841,7 +837,7 @@ public final class ContentService extends IContentService.Stub { try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - return syncManager.getIsSyncable( + return syncManager.computeSyncable( account, userId, providerName); } } finally { diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 39ddc3a98317..e0aac7b941a0 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -19,6 +19,7 @@ package com.android.server.content; import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; +import android.accounts.AccountManagerInternal; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AppGlobals; @@ -64,6 +65,7 @@ import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.PowerManager; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -79,6 +81,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerInternal; import com.google.android.collect.Lists; @@ -133,6 +136,8 @@ import java.util.Set; public class SyncManager { static final String TAG = "SyncManager"; + private static final boolean DEBUG_ACCOUNT_ACCESS = false; + /** Delay a sync due to local changes this long. In milliseconds */ private static final long LOCAL_SYNC_DELAY; @@ -194,6 +199,11 @@ public class SyncManager { private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; + + private static final int SYNC_OP_STATE_VALID = 0; + private static final int SYNC_OP_STATE_INVALID = 1; + private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2; + private Context mContext; private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; @@ -310,6 +320,10 @@ public class SyncManager { private final UserManager mUserManager; + private final AccountManager mAccountManager; + + private final AccountManagerInternal mAccountManagerInternal; + private List<UserInfo> getAllUsers() { return mUserManager.getUsers(); } @@ -490,8 +504,6 @@ public class SyncManager { @Override public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) { scheduleSync(info.account, info.userId, reason, info.provider, extras, - 0 /* no flexMillis */, - 0 /* run immediately */, false); } }); @@ -522,8 +534,7 @@ public class SyncManager { if (!removed) { scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_SERVICE_CHANGED, - type.authority, null, 0 /* no delay */, 0 /* no delay */, - false /* onlyThoseWithUnkownSyncableState */); + type.authority, null, false /* onlyThoseWithUnkownSyncableState */); } } }, mSyncHandler); @@ -562,6 +573,9 @@ public class SyncManager { } mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE); + mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class); + mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)); @@ -655,7 +669,7 @@ public class SyncManager { return mSyncStorageEngine; } - public int getIsSyncable(Account account, int userId, String providerName) { + private int getIsSyncable(Account account, int userId, String providerName) { int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName); UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId); @@ -666,22 +680,22 @@ public class SyncManager { RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( SyncAdapterType.newKey(providerName, account.type), userId); - if (syncAdapterInfo == null) return isSyncable; + if (syncAdapterInfo == null) return AuthorityInfo.NOT_SYNCABLE; PackageInfo pInfo = null; try { pInfo = AppGlobals.getPackageManager().getPackageInfo( syncAdapterInfo.componentName.getPackageName(), 0, userId); - if (pInfo == null) return isSyncable; + if (pInfo == null) return AuthorityInfo.NOT_SYNCABLE; } catch (RemoteException re) { // Shouldn't happen. - return isSyncable; + return AuthorityInfo.NOT_SYNCABLE; } if (pInfo.restrictedAccountType != null && pInfo.restrictedAccountType.equals(account.type)) { return isSyncable; } else { - return 0; + return AuthorityInfo.NOT_SYNCABLE; } } @@ -733,13 +747,10 @@ public class SyncManager { * @param extras a Map of SyncAdapter-specific information to control * syncs of a specific provider. Can be null. Is ignored * if the url is null. - * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run. - * @param runtimeMillis maximum milliseconds in the future to wait before performing sync. * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state. */ public void scheduleSync(Account requestedAccount, int userId, int reason, - String requestedAuthority, Bundle extras, long beforeRuntimeMillis, - long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) { + String requestedAuthority, Bundle extras, boolean onlyThoseWithUnkownSyncableState) { final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); if (extras == null) { extras = new Bundle(); @@ -749,17 +760,27 @@ public class SyncManager { + requestedAuthority); } - AccountAndUser[] accounts; - if (requestedAccount != null && userId != UserHandle.USER_ALL) { - accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; + AccountAndUser[] accounts = null; + if (requestedAccount != null) { + if (userId != UserHandle.USER_ALL) { + accounts = new AccountAndUser[]{new AccountAndUser(requestedAccount, userId)}; + } else { + for (AccountAndUser runningAccount : mRunningAccounts) { + if (requestedAccount.equals(runningAccount.account)) { + accounts = ArrayUtils.appendElement(AccountAndUser.class, + accounts, runningAccount); + } + } + } } else { accounts = mRunningAccounts; - if (accounts.length == 0) { - if (isLoggable) { - Slog.v(TAG, "scheduleSync: no accounts configured, dropping"); - } - return; + } + + if (ArrayUtils.isEmpty(accounts)) { + if (isLoggable) { + Slog.v(TAG, "scheduleSync: no accounts configured, dropping"); } + return; } final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); @@ -808,29 +829,41 @@ public class SyncManager { } for (String authority : syncableAuthorities) { - int isSyncable = getIsSyncable(account.account, account.userId, - authority); + int isSyncable = computeSyncable(account.account, account.userId, authority); + if (isSyncable == AuthorityInfo.NOT_SYNCABLE) { continue; } - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.account.type), account.userId); + + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = + mSyncAdapters.getServiceInfo(SyncAdapterType.newKey(authority, + account.account.type), account.userId); if (syncAdapterInfo == null) { continue; } + final int owningUid = syncAdapterInfo.uid; - final String owningPackage = syncAdapterInfo.componentName.getPackageName(); - try { - if (ActivityManagerNative.getDefault().getAppStartMode(owningUid, - owningPackage) == ActivityManager.APP_START_MODE_DISABLED) { - Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":" - + syncAdapterInfo.componentName - + " -- package not allowed to start"); - continue; + + if (isSyncable == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) { + if (isLoggable) { + Slog.v(TAG, " Not scheduling sync operation: " + + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); } - } catch (RemoteException e) { + Bundle finalExtras = new Bundle(extras); + mAccountManagerInternal.requestAccountAccess(account.account, + syncAdapterInfo.componentName.getPackageName(), + UserHandle.getUserId(owningUid), + new RemoteCallback((Bundle result) -> { + if (result != null + && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { + scheduleSync(account.account, userId, reason, authority, + finalExtras, onlyThoseWithUnkownSyncableState); + } + } + )); + continue; } + final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); if (isSyncable < 0 && isAlwaysSyncable) { @@ -838,6 +871,7 @@ public class SyncManager { account.account, account.userId, authority, AuthorityInfo.SYNCABLE); isSyncable = AuthorityInfo.SYNCABLE; } + if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { continue; } @@ -863,6 +897,9 @@ public class SyncManager { account.account, authority, account.userId); long delayUntil = mSyncStorageEngine.getDelayUntilTime(info); + + final String owningPackage = syncAdapterInfo.componentName.getPackageName(); + if (isSyncable < 0) { // Initialisation sync. Bundle newExtras = new Bundle(); @@ -887,8 +924,6 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, "scheduleSync:" + " delay until " + delayUntil - + " run by " + runtimeMillis - + " flexMillis " + beforeRuntimeMillis + ", source " + source + ", account " + account + ", authority " + authority @@ -904,6 +939,56 @@ public class SyncManager { } } + public int computeSyncable(Account account, int userId, String authority) { + final int status = getIsSyncable(account, userId, authority); + if (status == AuthorityInfo.NOT_SYNCABLE) { + return AuthorityInfo.NOT_SYNCABLE; + } + final SyncAdapterType type = SyncAdapterType.newKey(authority, account.type); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = + mSyncAdapters.getServiceInfo(type, userId); + if (syncAdapterInfo == null) { + return AuthorityInfo.NOT_SYNCABLE; + } + final int owningUid = syncAdapterInfo.uid; + final String owningPackage = syncAdapterInfo.componentName.getPackageName(); + try { + if (ActivityManagerNative.getDefault().getAppStartMode(owningUid, + owningPackage) == ActivityManager.APP_START_MODE_DISABLED) { + Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":" + + syncAdapterInfo.componentName + + " -- package not allowed to start"); + return AuthorityInfo.NOT_SYNCABLE; + } + } catch (RemoteException e) { + /* ignore - local call */ + } + if (!canAccessAccount(account, owningPackage, owningUid)) { + Log.w(TAG, "Access to " + account + " denied for package " + + owningPackage + " in UID " + syncAdapterInfo.uid); + return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS; + } + + return status; + } + + private boolean canAccessAccount(Account account, String packageName, int uid) { + if (mAccountManager.hasAccountAccess(account, packageName, + UserHandle.getUserHandleForUid(uid))) { + return true; + } + // We relax the account access rule to also include the system apps as + // they are trusted and we want to minimize the cases where the user + // involvement is required to grant access to the synced account. + try { + mContext.getPackageManager().getApplicationInfoAsUser(packageName, + PackageManager.MATCH_SYSTEM_ONLY, UserHandle.getUserId(uid)); + return true; + } catch (NameNotFoundException e) { + return false; + } + } + private void removeSyncsForAuthority(EndPoint info) { verifyJobScheduler(); List<SyncOperation> ops = getAllPendingSyncs(); @@ -960,8 +1045,6 @@ public class SyncManager { final Bundle extras = new Bundle(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); scheduleSync(account, userId, reason, authority, extras, - LOCAL_SYNC_DELAY /* earliest run time */, - 2 * LOCAL_SYNC_DELAY /* latest sync time. */, false /* onlyThoseWithUnkownSyncableState */); } @@ -1421,7 +1504,6 @@ public class SyncManager { mContext.getOpPackageName()); for (Account account : accounts) { scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, - 0 /* no delay */, 0 /* No flexMillis */, true /* onlyThoseWithUnknownSyncableState */); } } @@ -2530,13 +2612,18 @@ public class SyncManager { } } - if (isOperationValid(op)) { - if (!dispatchSyncOperation(op)) { + final int syncOpState = computeSyncOpState(op); + switch (syncOpState) { + case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: + case SYNC_OP_STATE_INVALID: { mSyncJobService.callJobFinished(op.jobId, false); - } - } else { + } return; + } + + if (!dispatchSyncOperation(op)) { mSyncJobService.callJobFinished(op.jobId, false); } + setAuthorityPendingState(op.target); } @@ -2596,8 +2683,7 @@ public class SyncManager { if (syncTargets != null) { scheduleSync(syncTargets.account, syncTargets.userId, - SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, 0, 0, - true); + SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, true); } } @@ -2665,6 +2751,26 @@ public class SyncManager { SyncStorageEngine.SOURCE_PERIODIC, extras, syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID, pollFrequencyMillis, flexMillis); + + final int syncOpState = computeSyncOpState(op); + switch (syncOpState) { + case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: { + mAccountManagerInternal.requestAccountAccess(op.target.account, + op.owningPackage, UserHandle.getUserId(op.owningUid), + new RemoteCallback((Bundle result) -> { + if (result != null + && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { + updateOrAddPeriodicSync(target, pollFrequency, flex, extras); + } + } + )); + } return; + + case SYNC_OP_STATE_INVALID: { + return; + } + } + scheduleSyncOperationH(op); mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } @@ -2725,29 +2831,38 @@ public class SyncManager { /** * Determine if a sync is no longer valid and should be dropped. */ - private boolean isOperationValid(SyncOperation op) { + private int computeSyncOpState(SyncOperation op) { final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); int state; final EndPoint target = op.target; - boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId); + // Drop the sync if the account of this operation no longer exists. AccountAndUser[] accounts = mRunningAccounts; if (!containsAccountAndUser(accounts, target.account, target.userId)) { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: account doesn't exist."); } - return false; + return SYNC_OP_STATE_INVALID; } // Drop this sync request if it isn't syncable. - state = getIsSyncable(target.account, target.userId, target.provider); - if (state == 0) { + state = computeSyncable(target.account, target.userId, target.provider); + if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) { if (isLoggable) { - Slog.v(TAG, " Dropping sync operation: isSyncable == 0."); + Slog.v(TAG, " Dropping sync operation: " + + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); } - return false; + return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS; } - syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically( - target.account, target.userId, target.provider); + if (state != AuthorityInfo.SYNCABLE) { + if (isLoggable) { + Slog.v(TAG, " Dropping sync operation: isSyncable != SYNCABLE"); + } + return SYNC_OP_STATE_INVALID; + } + + final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId) + && mSyncStorageEngine.getSyncAutomatically(target.account, + target.userId, target.provider); // We ignore system settings that specify the sync is invalid if: // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled. @@ -2760,9 +2875,9 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); } - return false; + return SYNC_OP_STATE_INVALID; } - return true; + return SYNC_OP_STATE_VALID; } private boolean dispatchSyncOperation(SyncOperation op) { diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index bc3fc6a47aef..64849aa321da 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -234,6 +234,12 @@ public class SyncStorageEngine extends Handler { */ public static final int SYNCABLE_NOT_INITIALIZED = 2; + /** + * The adapter is syncable but does not have access to the synced account and needs a + * user access approval. + */ + public static final int SYNCABLE_NO_ACCOUNT_ACCESS = 3; + final EndPoint target; final int ident; boolean enabled; diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 6a56fa6e629b..d25abbf1ff8a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -870,13 +870,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { IntentSender statusReceiver, int userId) { final int callingUid = Binder.getCallingUid(); mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); - boolean allowSilentUninstall = true; if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) { mAppOps.checkPackage(callingUid, callerPackageName); - final String installerPackageName = mPm.getInstallerPackageName(packageName); - allowSilentUninstall = mPm.isOrphaned(packageName) || - (installerPackageName != null - && installerPackageName.equals(callerPackageName)); } // Check whether the caller is device owner, in which case we do it silently. @@ -887,8 +882,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, packageName, isDeviceOwner, userId); - if (allowSilentUninstall && mContext.checkCallingOrSelfPermission( - android.Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) + == PackageManager.PERMISSION_GRANTED) { // Sweet, call straight through! mPm.deletePackage(packageName, adapter.getBinder(), userId, flags); } else if (isDeviceOwner) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 6cdc40f7a81c..583128444cfc 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -109,6 +109,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final int installerUid; final SessionParams params; final long createdMillis; + final int defaultContainerGid; /** Staging location where client data is written. */ final File stageDir; @@ -199,13 +200,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { + // Cache package manager data without the lock held + final PackageInfo pkgInfo = mPm.getPackageInfo( + params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId); + final ApplicationInfo appInfo = mPm.getApplicationInfo( + params.appPackageName, 0, userId); + synchronized (mLock) { if (msg.obj != null) { mRemoteObserver = (IPackageInstallObserver2) msg.obj; } try { - commitLocked(); + commitLocked(pkgInfo, appInfo); } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); @@ -264,6 +271,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { mPermissionsAccepted = false; } + final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, + PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + defaultContainerGid = UserHandle.getSharedAppGid(uid); } public SessionInfo generateInfo() { @@ -520,7 +530,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); } - private void commitLocked() throws PackageManagerException { + private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo) + throws PackageManagerException { if (mDestroyed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } @@ -538,7 +549,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that stage looks sane with respect to existing application. // This currently only ensures packageName, versionCode, and certificate // consistency. - validateInstallLocked(); + validateInstallLocked(pkgInfo, appInfo); Preconditions.checkNotNull(mPackageName); Preconditions.checkNotNull(mSignatures); @@ -650,7 +661,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * Note that upgrade compatibility is still performed by * {@link PackageManagerService}. */ - private void validateInstallLocked() throws PackageManagerException { + private void validateInstallLocked(PackageInfo pkgInfo, ApplicationInfo appInfo) + throws PackageManagerException { mPackageName = null; mVersionCode = -1; mSignatures = null; @@ -729,10 +741,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (removeSplitList.size() > 0) { // validate split names marked for removal - final int flags = mSignatures == null ? PackageManager.GET_SIGNATURES : 0; - final PackageInfo pkg = mPm.getPackageInfo(params.appPackageName, flags, userId); for (String splitName : removeSplitList) { - if (!ArrayUtils.contains(pkg.splitNames, splitName)) { + if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Split not found: " + splitName); } @@ -740,11 +750,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // ensure we've got appropriate package name, version code and signatures if (mPackageName == null) { - mPackageName = pkg.packageName; - mVersionCode = pkg.versionCode; + mPackageName = pkgInfo.packageName; + mVersionCode = pkgInfo.versionCode; } if (mSignatures == null) { - mSignatures = pkg.signatures; + mSignatures = pkgInfo.signatures; } } @@ -757,8 +767,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } else { // Partial installs must be consistent with existing install - final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); - if (app == null) { + if (appInfo == null) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package for " + mPackageName); } @@ -766,8 +775,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final PackageLite existing; final ApkLite existingBase; try { - existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0); - existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()), + existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0); + existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()), PackageParser.PARSE_COLLECT_CERTIFICATES); } catch (PackageParserException e) { throw PackageManagerException.from(e); @@ -777,7 +786,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Inherit base if not overridden if (mResolvedBaseFile == null) { - mResolvedBaseFile = new File(app.getBaseCodePath()); + mResolvedBaseFile = new File(appInfo.getBaseCodePath()); mResolvedInheritedFiles.add(mResolvedBaseFile); } @@ -794,7 +803,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } // Inherit compiled oat directory. - final File packageInstallDir = (new File(app.getBaseCodePath())).getParentFile(); + final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); mInheritedFilesBase = packageInstallDir; final File oatDir = new File(packageInstallDir, "oat"); if (oatDir.exists()) { @@ -822,7 +831,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { + private void assertApkConsistent(String tag, ApkLite apk) + throws PackageManagerException { if (!mPackageName.equals(apk.packageName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " + apk.packageName + " inconsistent with " + mPackageName); @@ -1035,10 +1045,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Failed to finalize container " + cid); } - final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); - final int gid = UserHandle.getSharedAppGid(uid); - if (!PackageHelper.fixSdPermissions(cid, gid, null)) { + if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) { throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, "Failed to fix permissions on container " + cid); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f326555ee00a..fa37177ad2bc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8366,6 +8366,10 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; } + if (isSystemApp(pkg)) { + pkgSetting.isOrphaned = true; + } + ArrayList<PackageParser.Package> clientLibPkgs = null; if ((scanFlags & SCAN_CHECK_ONLY) != 0) { @@ -8649,7 +8653,9 @@ public class PackageManagerService extends IPackageManager.Stub { for (i=0; i<N; i++) { PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i); PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name); - if (cur == null) { + final String curPackageName = cur == null ? null : cur.info.packageName; + final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName); + if (cur == null || isPackageUpdate) { mPermissionGroups.put(pg.info.name, pg); if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { @@ -8657,6 +8663,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { r.append(' '); } + if (isPackageUpdate) { + r.append("UPD:"); + } r.append(pg.info.name); } } else { @@ -15310,6 +15319,19 @@ public class PackageManagerService extends IPackageManager.Stub { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(observer); final int uid = Binder.getCallingUid(); + if (uid != Process.SHELL_UID && uid != Process.ROOT_UID && uid != Process.SYSTEM_UID + && uid != getPackageUid(mRequiredInstallerPackage, 0, UserHandle.getUserId(uid)) + && !isOrphaned(packageName) + && !isCallerSameAsInstaller(uid, packageName)) { + try { + final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); + intent.setData(Uri.fromParts("package", packageName, null)); + intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder()); + observer.onUserActionRequired(intent); + } catch (RemoteException re) { + } + return; + } final boolean deleteAllUsers = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0; final int[] users = deleteAllUsers ? sUserManager.getUserIds() : new int[]{ userId }; if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) { @@ -15378,6 +15400,12 @@ public class PackageManagerService extends IPackageManager.Stub { }); } + private boolean isCallerSameAsInstaller(int callingUid, String pkgName) { + final int installerPkgUid = getPackageUid(getInstallerPackageName(pkgName), + 0 /* flags */, UserHandle.getUserId(callingUid)); + return installerPkgUid == callingUid; + } + private int[] getBlockUninstallForUsers(String packageName, int[] userIds) { int[] result = EMPTY_INT_ARRAY; for (int userId : userIds) { diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 827b88a5b61a..6f6fd7c88dcd 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -667,6 +667,12 @@ class ShortcutPackage extends ShortcutPackageItem { // - version code hasn't change // - lastUpdateTime hasn't change // - all target activities are still enabled. + + // Note, system apps timestamps do *not* change after OTAs. (But they do + // after an adb sync or a local flash.) + // This means if a system app's version code doesn't change on an OTA, + // we don't notice it's updated. But that's fine since their version code *should* + // really change on OTAs. if ((getPackageInfo().getVersionCode() == pi.versionCode) && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime) && areAllActivitiesStillEnabled()) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index b80775b690e2..adf19dc8295e 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -54,6 +54,7 @@ import android.graphics.RectF; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -2659,10 +2660,14 @@ public class ShortcutService extends IShortcutService.Stub { boolean forceRescan) { final ShortcutUser user = getUserShortcutsLocked(userId); + // Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime + // is not reliable. final long now = injectCurrentTimeMillis(); + final boolean afterOta = + !injectBuildFingerprint().equals(user.getLastAppScanOsFingerprint()); // Then for each installed app, publish manifest shortcuts when needed. - forUpdatedPackages(userId, lastScanTime, ai -> { + forUpdatedPackages(userId, lastScanTime, afterOta, ai -> { user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId); user.rescanPackageIfNeeded(ai.packageName, forceRescan); }); @@ -2670,6 +2675,7 @@ public class ShortcutService extends IShortcutService.Stub { // Write the time just before the scan, because there may be apps that have just // been updated, and we want to catch them in the next time. user.setLastAppScanTime(now); + user.setLastAppScanOsFingerprint(injectBuildFingerprint()); scheduleSaveUser(userId); } @@ -2908,7 +2914,7 @@ public class ShortcutService extends IShortcutService.Stub { return parceledList.getList(); } - private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, + private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta, Consumer<ApplicationInfo> callback) { if (DEBUG) { Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime); @@ -2920,7 +2926,8 @@ public class ShortcutService extends IShortcutService.Stub { // If the package has been updated since the last scan time, then scan it. // Also if it's a system app with no update, lastUpdateTime is not reliable, so // just scan it. - if (pi.lastUpdateTime >= lastScanTime || isPureSystemApp(pi.applicationInfo)) { + if (pi.lastUpdateTime >= lastScanTime + || (afterOta && isPureSystemApp(pi.applicationInfo))) { if (DEBUG) { Slog.d(TAG, "Found updated package " + pi.packageName); } @@ -3598,6 +3605,12 @@ public class ShortcutService extends IShortcutService.Stub { Binder.restoreCallingIdentity(token); } + // Injection point. + @VisibleForTesting + String injectBuildFingerprint() { + return Build.FINGERPRINT; + } + final void wtf(String message) { wtf(message, /* exception= */ null); } diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index ce3ed9c7deef..c05c66fedbf8 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -60,6 +60,7 @@ class ShortcutUser { // Suffix "2" was added to force rescan all packages after the next OTA. private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time2"; + private static final String ATTR_LAST_APP_SCAN_OS_FINGERPRINT = "last-app-scan-fp"; private static final String KEY_USER_ID = "userId"; private static final String KEY_LAUNCHERS = "launchers"; private static final String KEY_PACKAGES = "packages"; @@ -125,6 +126,8 @@ class ShortcutUser { private long mLastAppScanTime; + private String mLastAppScanOsFingerprint; + public ShortcutUser(ShortcutService service, int userId) { mService = service; mUserId = userId; @@ -142,6 +145,14 @@ class ShortcutUser { mLastAppScanTime = lastAppScanTime; } + public String getLastAppScanOsFingerprint() { + return mLastAppScanOsFingerprint; + } + + public void setLastAppScanOsFingerprint(String lastAppScanOsFingerprint) { + mLastAppScanOsFingerprint = lastAppScanOsFingerprint; + } + // We don't expose this directly to non-test code because only ShortcutUser should add to/ // remove from it. @VisibleForTesting @@ -318,6 +329,8 @@ class ShortcutUser { ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales); ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, mLastAppScanTime); + ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_OS_FINGERPRINT, + mLastAppScanOsFingerprint); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); @@ -364,7 +377,8 @@ class ShortcutUser { ATTR_LAST_APP_SCAN_TIME); final long currentTime = s.injectCurrentTimeMillis(); ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0; - + ret.mLastAppScanOsFingerprint = ShortcutService.parseStringAttribute(parser, + ATTR_LAST_APP_SCAN_OS_FINGERPRINT); final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -457,6 +471,8 @@ class ShortcutUser { pw.print(mLastAppScanTime); pw.print("] "); pw.print(ShortcutService.formatTime(mLastAppScanTime)); + pw.print(" Last app scan FP: "); + pw.print(mLastAppScanOsFingerprint); pw.println(); prefix += prefix + " "; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 8da3a7d7639d..a39add80ba8f 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -7756,6 +7756,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } final WindowState w = mTopFullscreenOpaqueWindowState; + if (w != mFocusedWindow) { + return false; + } // We only enable seamless rotation if the top window has requested // it and is in the fullscreen opaque state. Seamless rotation diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 64f79e165e75..3fd4b37c6f42 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1350,7 +1350,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { @Override public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, - IWallpaperManagerCallback completion) { + IWallpaperManagerCallback completion, int userId) { + userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, + false /* all */, true /* full */, "changing wallpaper", null /* pkg */); checkPermission(android.Manifest.permission.SET_WALLPAPER); if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) { @@ -1374,8 +1376,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } - final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which)); WallpaperData wallpaper; @@ -1757,8 +1757,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } WallpaperData wallpaper = (which == FLAG_LOCK) - ? mWallpaperMap.get(userId) - : mLockWallpaperMap.get(userId); + ? mLockWallpaperMap.get(userId) + : mWallpaperMap.get(userId); return (wallpaper != null) ? wallpaper.allowBackup : false; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f2f85bf184cf..1f82f0cda9f6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2908,12 +2908,11 @@ public class WindowManagerService extends IWindowManager.Stub } result |= RELAYOUT_RES_SURFACE_CHANGED; } - final WindowSurfaceController surfaceController = winAnimator.mSurfaceController; - if (viewVisibility == View.VISIBLE && surfaceController != null) { + if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) { // We already told the client to go invisible, but the message may not be // handled yet, or it might want to draw a last frame. If we already have a // surface, let the client use that, but don't create new surface at this point. - surfaceController.getSurface(outSurface); + winAnimator.mSurfaceController.getSurface(outSurface); } else { if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index e96e97bcaa02..792f3001c049 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -407,6 +407,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override + String injectBuildFingerprint() { + return mInjectedBuildFingerprint; + } + + @Override void wtf(String message, Throwable th) { // During tests, WTF is fatal. fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th)); @@ -528,6 +533,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected Map<String, PackageInfo> mInjectedPackages; protected Set<PackageWithUser> mUninstalledPackages; + protected Set<String> mSystemPackages; protected PackageManager mMockPackageManager; protected PackageManagerInternal mMockPackageManagerInternal; @@ -628,6 +634,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback"; protected static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999; + protected String mInjectedBuildFingerprint = "build1"; + static { QUERY_ALL.setQueryFlags( ShortcutQuery.FLAG_GET_ALL_KINDS); @@ -677,6 +685,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); mUninstalledPackages = new HashSet<>(); + mSystemPackages = new HashSet<>(); mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files"); @@ -963,6 +972,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) { ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; } + if (mSystemPackages.contains(packageName)) { + ret.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + } if (getSignatures) { ret.signatures = pi.signatures; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index ed4e391a6273..46ac7ce88b90 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -4347,6 +4347,128 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } + public void testHandlePackageUpdate_systemAppUpdate() { + + // Package1 is a system app. Package 2 is not a system app, so it's not scanned + // in this test at all. + mSystemPackages.add(CALLING_PACKAGE_1); + + // Initial state: no shortcuts. + mService.checkPackageChanges(USER_0); + + assertEquals(mInjectedCurrentTimeMillis, + mService.getUserShortcutsLocked(USER_0).getLastAppScanTime()); + assertEquals(mInjectedBuildFingerprint, + mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint()); + + // They have no shortcuts. + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Next. + // Update the packages -- now they have 1 manifest shortcut. + // But checkPackageChanges() don't notice it, since their version code / timestamp haven't + // changed. + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_1); + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()), + R.xml.shortcut_1); + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Next. + // Update the build finger print. All system apps will be scanned now. + mInjectedBuildFingerprint = "update1"; + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Next. + // Update manifest shortcuts. + mInjectedBuildFingerprint = "update2"; + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + // Fingerprint hasn't changed, so CALLING_PACKAGE_1 wasn't scanned. + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Update the fingerprint, but CALLING_PACKAGE_1's version code hasn't changed, so + // still not scanned. + mInjectedBuildFingerprint = "update2"; + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Now update the version code, so CALLING_PACKAGE_1 is scanned again. + mInjectedBuildFingerprint = "update3"; + mInjectedCurrentTimeMillis += 1000; + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Make sure getLastAppScanTime / getLastAppScanOsFingerprint are persisted. + initService(); + assertEquals(mInjectedCurrentTimeMillis, + mService.getUserShortcutsLocked(USER_0).getLastAppScanTime()); + assertEquals(mInjectedBuildFingerprint, + mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint()); + } + public void testHandlePackageChanged() { final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1"); final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2"); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 43d2a1f1156c..a04034e3f764 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -535,6 +535,18 @@ public class VoiceInteractionManagerService extends SystemService { + " user=" + userHandle); } + ComponentName getCurAssistant(int userHandle) { + String curAssistant = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.ASSISTANT, userHandle); + if (TextUtils.isEmpty(curAssistant)) { + return null; + } + if (DEBUG) Slog.d(TAG, "getCurAssistant curAssistant=" + curAssistant + + " user=" + userHandle); + return ComponentName.unflattenFromString(curAssistant); + } + void resetCurAssistant(int userHandle) { Settings.Secure.putStringForUser(mContext.getContentResolver(), Settings.Secure.ASSISTANT, null, userHandle); @@ -1178,6 +1190,7 @@ public class VoiceInteractionManagerService extends SystemService { synchronized (VoiceInteractionManagerServiceStub.this) { ComponentName curInteractor = getCurInteractor(userHandle); ComponentName curRecognizer = getCurRecognizer(userHandle); + ComponentName curAssistant = getCurAssistant(userHandle); if (curRecognizer == null) { // Could a new recognizer appear when we don't have one pre-installed? if (anyPackagesAppearing()) { @@ -1196,6 +1209,7 @@ public class VoiceInteractionManagerService extends SystemService { // the default config. setCurInteractor(null, userHandle); setCurRecognizer(null, userHandle); + resetCurAssistant(userHandle); initForUser(userHandle); return; } @@ -1212,6 +1226,20 @@ public class VoiceInteractionManagerService extends SystemService { return; } + if (curAssistant != null) { + int change = isPackageDisappearing(curAssistant.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE) { + // If the currently set assistant is being removed, then we should + // reset back to the default state (which is probably that we prefer + // to have the default full voice interactor enabled). + setCurInteractor(null, userHandle); + setCurRecognizer(null, userHandle); + resetCurAssistant(userHandle); + initForUser(userHandle); + return; + } + } + // There is no interactor, so just deal with a simple recognizer. int change = isPackageDisappearing(curRecognizer.getPackageName()); if (change == PACKAGE_PERMANENT_CHANGE |