diff options
Diffstat (limited to 'core/java/android')
32 files changed, 1123 insertions, 489 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 85918cf..4cff12f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -34,6 +34,7 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; @@ -44,6 +45,7 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -378,19 +380,41 @@ final class ApplicationPackageManager extends PackageManager { throw new NameNotFoundException("No shared userid for user:"+sharedUserName); } + @SuppressWarnings("unchecked") @Override public List<PackageInfo> getInstalledPackages(int flags) { try { - return mPM.getInstalledPackages(flags); + final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>(); + PackageInfo lastItem = null; + ParceledListSlice<PackageInfo> slice; + + do { + final String lastKey = lastItem != null ? lastItem.packageName : null; + slice = mPM.getInstalledPackages(flags, lastKey); + lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR); + } while (!slice.isLastSlice()); + + return packageInfos; } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } + @SuppressWarnings("unchecked") @Override public List<ApplicationInfo> getInstalledApplications(int flags) { try { - return mPM.getInstalledApplications(flags); + final List<ApplicationInfo> applicationInfos = new ArrayList<ApplicationInfo>(); + ApplicationInfo lastItem = null; + ParceledListSlice<ApplicationInfo> slice; + + do { + final String lastKey = lastItem != null ? lastItem.packageName : null; + slice = mPM.getInstalledApplications(flags, lastKey); + lastItem = slice.populateList(applicationInfos, ApplicationInfo.CREATOR); + } while (!slice.isLastSlice()); + + return applicationInfos; } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index db8d5e9..9cb57be 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -29,12 +29,13 @@ import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.os.SystemClock; import android.speech.RecognizerIntent; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; +import android.util.TypedValue; +import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; @@ -50,9 +51,6 @@ import android.widget.LinearLayout; import android.widget.SearchView; import android.widget.TextView; -import java.util.WeakHashMap; -import java.util.concurrent.atomic.AtomicLong; - /** * Search dialog. This is controlled by the * SearchManager and runs in the current foreground process. @@ -110,13 +108,20 @@ public class SearchDialog extends Dialog { } }; + static int resolveDialogTheme(Context context) { + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(com.android.internal.R.attr.searchDialogTheme, + outValue, true); + return outValue.resourceId; + } + /** * Constructor - fires it up and makes it look like the search UI. * * @param context Application Context we can use for system acess */ public SearchDialog(Context context, SearchManager searchManager) { - super(context, com.android.internal.R.style.Theme_SearchBar); + super(context, resolveDialogTheme(context)); // Save voice intent for later queries/launching mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); @@ -642,6 +647,14 @@ public class SearchDialog extends Dialog { } return super.dispatchKeyEventPreIme(event); } + + /** + * Don't allow action modes in a SearchBar, it looks silly. + */ + @Override + public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { + return null; + } } private boolean isEmpty(AutoCompleteTextView actv) { diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 64c437d..4285388 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -340,6 +340,12 @@ public class ActivityInfo extends ComponentInfo public static final int CONFIG_SCREEN_SIZE = 0x0400; /** * Bit in {@link #configChanges} that indicates that the activity + * can itself handle the smallest screen size. Set from the + * {@link android.R.attr#configChanges} attribute. + */ + public static final int CONFIG_SMALLEST_SCREEN_SIZE = 0x0800; + /** + * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the font scaling factor. Set from the * {@link android.R.attr#configChanges} attribute. This is * not a core resource configutation, but a higher-level value, so its @@ -364,6 +370,7 @@ public class ActivityInfo extends ComponentInfo 0x0800, // SCREEN LAYOUT 0x1000, // UI MODE 0x0200, // SCREEN SIZE + 0x2000, // SMALLEST SCREEN SIZE }; /** @hide * Convert Java change bits to native. diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 11cd446..37b6822 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -30,6 +30,7 @@ import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; +import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; @@ -110,9 +111,21 @@ interface IPackageManager { List<ResolveInfo> queryIntentServices(in Intent intent, String resolvedType, int flags); - List<PackageInfo> getInstalledPackages(int flags); + /** + * This implements getInstalledPackages via a "last returned row" + * mechanism that is not exposed in the API. This is to get around the IPC + * limit that kicks in when flags are included that bloat up the data + * returned. + */ + ParceledListSlice getInstalledPackages(int flags, in String lastRead); - List<ApplicationInfo> getInstalledApplications(int flags); + /** + * This implements getInstalledApplications via a "last returned row" + * mechanism that is not exposed in the API. This is to get around the IPC + * limit that kicks in when flags are included that bloat up the data + * returned. + */ + ParceledListSlice getInstalledApplications(int flags, in String lastRead); /** * Retrieve all applications that are marked as persistent. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b8cb165..9ff324b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -396,7 +396,7 @@ public class PackageParser { int cookie = assmgr.addAssetPath(mArchiveSourcePath); if (cookie != 0) { res = new Resources(assmgr, metrics, null); - assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); assetError = false; @@ -596,7 +596,7 @@ public class PackageParser { AssetManager assmgr = null; try { assmgr = new AssetManager(); - assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); int cookie = assmgr.addAssetPath(packageFilePath); parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); @@ -1942,8 +1942,9 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_configChanges, 0); if (owner.applicationInfo.targetSdkVersion - < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - a.info.configChanges |= ActivityInfo.CONFIG_SCREEN_SIZE; + < android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { + a.info.configChanges |= ActivityInfo.CONFIG_SCREEN_SIZE + | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; } a.info.softInputMode = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode, diff --git a/core/java/android/content/pm/ParceledListSlice.aidl b/core/java/android/content/pm/ParceledListSlice.aidl new file mode 100755 index 0000000..c02cc6a --- /dev/null +++ b/core/java/android/content/pm/ParceledListSlice.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2011, 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.content.pm; + +parcelable ParceledListSlice; diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java new file mode 100644 index 0000000..f3a98db --- /dev/null +++ b/core/java/android/content/pm/ParceledListSlice.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 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.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Builds up a parcel that is discarded when written to another parcel or + * written to a list. This is useful for API that sends huge lists across a + * Binder that may be larger than the IPC limit. + * + * @hide + */ +public class ParceledListSlice<T extends Parcelable> implements Parcelable { + /* + * TODO get this number from somewhere else. For now set it to a quarter of + * the 1MB limit. + */ + private static final int MAX_IPC_SIZE = 256 * 1024; + + private Parcel mParcel; + + private int mNumItems; + + private boolean mIsLastSlice; + + public ParceledListSlice() { + mParcel = Parcel.obtain(); + } + + private ParceledListSlice(Parcel p, int numItems, boolean lastSlice) { + mParcel = p; + mNumItems = numItems; + mIsLastSlice = lastSlice; + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Write this to another Parcel. Note that this discards the internal Parcel + * and should not be used anymore. This is so we can pass this to a Binder + * where we won't have a chance to call recycle on this. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mNumItems); + dest.writeInt(mIsLastSlice ? 1 : 0); + + if (mNumItems > 0) { + final int parcelSize = mParcel.dataSize(); + dest.writeInt(parcelSize); + dest.appendFrom(mParcel, 0, parcelSize); + } + + mNumItems = 0; + mParcel.recycle(); + mParcel = null; + } + + /** + * Appends a parcel to this list slice. + * + * @param item Parcelable item to append to this list slice + * @return true when the list slice is full and should not be appended to + * anymore + */ + public boolean append(T item) { + if (mParcel == null) { + throw new IllegalStateException("ParceledListSlice has already been recycled"); + } + + item.writeToParcel(mParcel, PARCELABLE_WRITE_RETURN_VALUE); + mNumItems++; + + return mParcel.dataSize() > MAX_IPC_SIZE; + } + + /** + * Populates a list and discards the internal state of the + * ParceledListSlice in the process. The instance should + * not be used anymore. + * + * @param list list to insert items from this slice. + * @param creator creator that knows how to unparcel the + * target object type. + * @return the last item inserted into the list or null if none. + */ + public T populateList(List<T> list, Creator<T> creator) { + mParcel.setDataPosition(0); + + T item = null; + for (int i = 0; i < mNumItems; i++) { + item = creator.createFromParcel(mParcel); + list.add(item); + } + + mParcel.recycle(); + mParcel = null; + + return item; + } + + /** + * Sets whether this is the last list slice in the series. + * + * @param lastSlice + */ + public void setLastSlice(boolean lastSlice) { + mIsLastSlice = lastSlice; + } + + /** + * Returns whether this is the last slice in a series of slices. + * + * @return true if this is the last slice in the series. + */ + public boolean isLastSlice() { + return mIsLastSlice; + } + + @SuppressWarnings("unchecked") + public static final Parcelable.Creator<ParceledListSlice> CREATOR = + new Parcelable.Creator<ParceledListSlice>() { + public ParceledListSlice createFromParcel(Parcel in) { + final int numItems = in.readInt(); + final boolean lastSlice = in.readInt() == 1; + + if (numItems > 0) { + final int parcelSize = in.readInt(); + + // Advance within this Parcel + int offset = in.dataPosition(); + in.setDataPosition(offset + parcelSize); + + Parcel p = Parcel.obtain(); + p.setDataPosition(0); + p.appendFrom(in, offset, parcelSize); + p.setDataPosition(0); + + return new ParceledListSlice(p, numItems, lastSlice); + } else { + return new ParceledListSlice(); + } + } + + public ParceledListSlice[] newArray(int size) { + return new ParceledListSlice[size]; + } + }; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index be67e96..931cb18 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -652,8 +652,8 @@ public final class AssetManager { public native final void setConfiguration(int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, - int majorVersion); + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, + int screenLayout, int uiMode, int majorVersion); /** * Retrieve the resource identifier for the given resource name. diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 0de08f2..12ec258 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -259,6 +259,15 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public int screenHeightDp; + public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; + + /** + * The smallest screen size an application will see in normal operation. + * This is the smallest value of both screenWidthDp and screenHeightDp + * in both portrait and landscape. + */ + public int smallestScreenWidthDp; + /** * @hide Internal book-keeping. */ @@ -298,6 +307,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration uiMode = o.uiMode; screenWidthDp = o.screenWidthDp; screenHeightDp = o.screenHeightDp; + smallestScreenWidthDp = o.smallestScreenWidthDp; seq = o.seq; } @@ -315,6 +325,56 @@ public final class Configuration implements Parcelable, Comparable<Configuration } else { sb.append(" (no locale)"); } + if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { + sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp"); + } else { + sb.append("?swdp"); + } + if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) { + sb.append(" w"); sb.append(screenWidthDp); sb.append("dp"); + } else { + sb.append("?wdp"); + } + if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) { + sb.append(" h"); sb.append(screenHeightDp); sb.append("dp"); + } else { + sb.append("?hdp"); + } + switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) { + case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break; + case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break; + case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break; + case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break; + case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break; + default: sb.append(" layoutSize="); + sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break; + } + switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) { + case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break; + case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break; + case SCREENLAYOUT_LONG_YES: sb.append(" long"); break; + default: sb.append(" layoutLong="); + sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break; + } + switch (orientation) { + case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break; + case ORIENTATION_LANDSCAPE: sb.append(" land"); break; + case ORIENTATION_PORTRAIT: sb.append(" port"); break; + default: sb.append(" orien="); sb.append(orientation); break; + } + switch ((uiMode&UI_MODE_TYPE_MASK)) { + case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break; + case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break; + case UI_MODE_TYPE_DESK: sb.append(" desk"); break; + case UI_MODE_TYPE_CAR: sb.append(" car"); break; + default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; + } + switch ((uiMode&UI_MODE_NIGHT_MASK)) { + case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break; + case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break; + case UI_MODE_NIGHT_YES: sb.append(" night"); break; + default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break; + } switch (touchscreen) { case TOUCHSCREEN_UNDEFINED: sb.append(" ?touch"); break; case TOUCHSCREEN_NOTOUCH: sb.append(" -touch"); break; @@ -356,51 +416,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration case NAVIGATIONHIDDEN_YES: sb.append("/h"); break; default: sb.append("/"); sb.append(navigationHidden); break; } - switch (orientation) { - case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break; - case ORIENTATION_LANDSCAPE: sb.append(" land"); break; - case ORIENTATION_PORTRAIT: sb.append(" port"); break; - default: sb.append(" orien="); sb.append(orientation); break; - } - switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) { - case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break; - case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break; - case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break; - case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break; - case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break; - default: sb.append(" layoutSize="); - sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break; - } - switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) { - case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break; - case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break; - case SCREENLAYOUT_LONG_YES: sb.append(" long"); break; - default: sb.append(" layoutLong="); - sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break; - } - switch ((uiMode&UI_MODE_TYPE_MASK)) { - case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break; - case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break; - case UI_MODE_TYPE_DESK: sb.append(" desk"); break; - case UI_MODE_TYPE_CAR: sb.append(" car"); break; - default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; - } - switch ((uiMode&UI_MODE_NIGHT_MASK)) { - case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break; - case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break; - case UI_MODE_NIGHT_YES: sb.append(" night"); break; - default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break; - } - if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) { - sb.append(" w"); sb.append(screenWidthDp); sb.append("dp"); - } else { - sb.append("?wdp"); - } - if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) { - sb.append(" h"); sb.append(screenHeightDp); sb.append("dp"); - } else { - sb.append("?hdp"); - } if (seq != 0) { sb.append(" s."); sb.append(seq); @@ -428,6 +443,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration uiMode = UI_MODE_TYPE_UNDEFINED; screenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; screenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; + smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; seq = 0; } @@ -531,6 +547,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_SCREEN_SIZE; screenHeightDp = delta.screenHeightDp; } + if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED + && smallestScreenWidthDp != delta.smallestScreenWidthDp) { + changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + smallestScreenWidthDp = delta.smallestScreenWidthDp; + } if (delta.seq != 0) { seq = delta.seq; @@ -564,7 +585,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}, or * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE - * PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}. + * PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or + * {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE + * PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}. */ public int diff(Configuration delta) { int changed = 0; @@ -625,6 +648,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration && screenHeightDp != delta.screenHeightDp) { changed |= ActivityInfo.CONFIG_SCREEN_SIZE; } + if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED + && smallestScreenWidthDp != delta.smallestScreenWidthDp) { + changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + } return changed; } @@ -708,6 +735,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(uiMode); dest.writeInt(screenWidthDp); dest.writeInt(screenHeightDp); + dest.writeInt(smallestScreenWidthDp); dest.writeInt(seq); } @@ -731,6 +759,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration uiMode = source.readInt(); screenWidthDp = source.readInt(); screenHeightDp = source.readInt(); + smallestScreenWidthDp = source.readInt(); seq = source.readInt(); } @@ -795,6 +824,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration n = this.screenWidthDp - that.screenWidthDp; if (n != 0) return n; n = this.screenHeightDp - that.screenHeightDp; + if (n != 0) return n; + n = this.smallestScreenWidthDp - that.smallestScreenWidthDp; //if (n != 0) return n; return n; } @@ -830,6 +861,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration result = 31 * result + uiMode; result = 31 * result + screenWidthDp; result = 31 * result + screenHeightDp; + result = 31 * result + smallestScreenWidthDp; return result; } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 540f704..a072e94 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1458,6 +1458,7 @@ public class Resources { mConfiguration.touchscreen, (int)(mMetrics.density*160), mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, + mConfiguration.smallestScreenWidthDp, mConfiguration.screenWidthDp, mConfiguration.screenHeightDp, mConfiguration.screenLayout, mConfiguration.uiMode, Build.VERSION.RESOURCES_SDK_INT); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 6d955e7..d5c4ace 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1181,6 +1181,8 @@ public class Camera { private static final String KEY_EXPOSURE_COMPENSATION_STEP = "exposure-compensation-step"; private static final String KEY_AUTO_EXPOSURE_LOCK = "auto-exposure-lock"; private static final String KEY_AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported"; + private static final String KEY_AUTO_WHITEBALANCE_LOCK = "auto-whitebalance-lock"; + private static final String KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported"; private static final String KEY_METERING_AREAS = "metering-areas"; private static final String KEY_MAX_NUM_METERING_AREAS = "max-num-metering-areas"; private static final String KEY_ZOOM = "zoom"; @@ -2468,37 +2470,47 @@ public class Camera { } /** - * Sets the auto-exposure lock state. Applications should check - * {@link #isAutoExposureLockSupported} before using this method. + * <p>Sets the auto-exposure lock state. Applications should check + * {@link #isAutoExposureLockSupported} before using this method.</p> * - * If set to true, the camera auto-exposure routine will pause until the - * lock is set to false. Exposure compensation settings changes will - * still take effect while auto-exposure is locked. Stopping preview - * with {@link #stopPreview()}, or triggering still image capture with - * {@link #takePicture(Camera.ShutterCallback, Camera.PictureCallback, - * Camera.PictureCallback)}, will automatically set the lock to - * false. However, the lock can be re-enabled before preview is - * re-started to keep the same AE parameters. Exposure compensation, in - * conjunction with re-enabling the AE lock after each still capture, - * can be used to capture an exposure-bracketed burst of images, for - * example. Auto-exposure state, including the lock state, will not be + * <p>If set to true, the camera auto-exposure routine will immediately + * pause until the lock is set to false. Exposure compensation settings + * changes will still take effect while auto-exposure is locked.</p> + * + * <p>If auto-exposure is already locked, setting this to true again has + * no effect (the driver will not recalculate exposure values).</p> + * + * <p>Stopping preview with {@link #stopPreview()}, or triggering still + * image capture with {@link #takePicture(Camera.ShutterCallback, + * Camera.PictureCallback, Camera.PictureCallback)}, will automatically + * set the lock to false. However, the lock can be re-enabled before + * preview is re-started to keep the same AE parameters.</p> + * + * <p>Exposure compensation, in conjunction with re-enabling the AE and + * AWB locks after each still capture, can be used to capture an + * exposure-bracketed burst of images, for example.</p> + * + * <p>Auto-exposure state, including the lock state, will not be * maintained after camera {@link #release()} is called. Locking * auto-exposure after {@link #open()} but before the first call to * {@link #startPreview()} will not allow the auto-exposure routine to - * run at all, and may result in severely over- or under-exposed images. + * run at all, and may result in severely over- or under-exposed + * images.</p> * - * The driver may also independently lock auto-exposure after auto-focus - * completes. If this is undesirable, be sure to always set the - * auto-exposure lock to false after the + * <p>The driver may also independently lock auto-exposure after + * auto-focus completes. If this is undesirable, be sure to always set + * the auto-exposure lock to false after the * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is * received. The {@link #getAutoExposureLock()} method can be used after * the callback to determine if the camera has locked auto-exposure - * independently. + * independently.</p> * * @param toggle new state of the auto-exposure lock. True means that * auto-exposure is locked, false means that the auto-exposure * routine is free to run normally. * + * @see #getAutoExposureLock() + * * @hide */ public void setAutoExposureLock(boolean toggle) { @@ -2542,6 +2554,91 @@ public class Camera { } /** + * <p>Sets the auto-white balance lock state. Applications should check + * {@link #isAutoWhiteBalanceLockSupported} before using this + * method.</p> + * + * <p>If set to true, the camera auto-white balance routine will + * immediately pause until the lock is set to false.</p> + * + * <p>If auto-white balance is already locked, setting this to true + * again has no effect (the driver will not recalculate white balance + * values).</p> + * + * <p>Stopping preview with {@link #stopPreview()}, or triggering still + * image capture with {@link #takePicture(Camera.ShutterCallback, + * Camera.PictureCallback, Camera.PictureCallback)}, will automatically + * set the lock to false. However, the lock can be re-enabled before + * preview is re-started to keep the same white balance parameters.</p> + * + * <p>Exposure compensation, in conjunction with re-enabling the AE and + * AWB locks after each still capture, can be used to capture an + * exposure-bracketed burst of images, for example. Auto-white balance + * state, including the lock state, will not be maintained after camera + * {@link #release()} is called. Locking auto-white balance after + * {@link #open()} but before the first call to {@link #startPreview()} + * will not allow the auto-white balance routine to run at all, and may + * result in severely incorrect color in captured images.</p> + * + * <p>The driver may also independently lock auto-white balance after + * auto-focus completes. If this is undesirable, be sure to always set + * the auto-white balance lock to false after the + * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is + * received. The {@link #getAutoWhiteBalanceLock()} method can be used + * after the callback to determine if the camera has locked auto-white + * balance independently.</p> + * + * @param toggle new state of the auto-white balance lock. True means + * that auto-white balance is locked, false means that the + * auto-white balance routine is free to run normally. + * + * @see #getAutoWhiteBalanceLock() + * + * @hide + */ + public void setAutoWhiteBalanceLock(boolean toggle) { + set(KEY_AUTO_WHITEBALANCE_LOCK, toggle ? TRUE : FALSE); + } + + /** + * Gets the state of the auto-white balance lock. Applications should + * check {@link #isAutoWhiteBalanceLockSupported} before using this + * method. See {@link #setAutoWhiteBalanceLock} for details about the + * lock. + * + * @return State of the auto-white balance lock. Returns true if + * auto-white balance is currently locked, and false + * otherwise. The auto-white balance lock may be independently + * enabled by the camera subsystem when auto-focus has + * completed. This method can be used after the + * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} + * callback to determine if the camera has locked AWB. + * + * @see #setAutoWhiteBalanceLock(boolean) + * + * @hide + */ + public boolean getAutoWhiteBalanceLock() { + String str = get(KEY_AUTO_WHITEBALANCE_LOCK); + return TRUE.equals(str); + } + + /** + * Returns true if auto-white balance locking is supported. Applications + * should call this before trying to lock auto-white balance. See + * {@link #setAutoWhiteBalanceLock} for details about the lock. + * + * @return true if auto-white balance lock is supported. + * @see #setAutoWhiteBalanceLock(boolean) + * + * @hide + */ + public boolean isAutoWhiteBalanceLockSupported() { + String str = get(KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED); + return TRUE.equals(str); + } + + /** * Gets current zoom value. This also works when smooth zoom is in * progress. Applications should check {@link #isZoomSupported} before * using this method. diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index a4ba3bd..68fc101 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -104,6 +104,13 @@ public class Sensor { */ public static final int TYPE_ROTATION_VECTOR = 11; + /** + * A constant describing a relative humidity sensor type. + * See {@link android.hardware.SensorEvent SensorEvent} + * for more details. + */ + public static final int TYPE_RELATIVE_HUMIDITY = 12; + /** A constant describing an ambient temperature sensor type */ public static final int TYPE_AMBIENT_TEMPERATURE = 13; diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index b111b84..0411b5c 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -327,6 +327,64 @@ public class SensorEvent { * in the clockwise direction (mathematically speaking, it should be * positive in the counter-clockwise direction). * </p> + * + * <h4>{@link android.hardware.Sensor#TYPE_RELATIVE_HUMIDITY + * Sensor.TYPE_RELATIVE_HUMIDITY}:</h4> + * <ul> + * <p> + * values[0]: Relative ambient air humidity in percent + * </p> + * </ul> + * <p> + * When relative ambient air humidity and ambient temperature are + * measured, the dew point and absolute humidity can be calculated. + * </p> + * <u>Dew Point</u> + * <p> + * The dew point is the temperature to which a given parcel of air must be + * cooled, at constant barometric pressure, for water vapor to condense + * into water. + * </p> + * <center><pre> + * ln(RH/100%) + m·t/(T<sub>n</sub>+t) + * t<sub>d</sub>(t,RH) = T<sub>n</sub> · ------------------------------ + * m - [ln(RH/100%) + m·t/(T<sub>n</sub>+t)] + * </pre></center> + * <dl> + * <dt>t<sub>d</sub></dt> <dd>dew point temperature in °C</dd> + * <dt>t</dt> <dd>actual temperature in °C</dd> + * <dt>RH</dt> <dd>actual relative humidity in %</dd> + * <dt>m</dt> <dd>17.62</dd> + * <dt>T<sub>n</sub></dt> <dd>243.12 °C</dd> + * </dl> + * <p>for example:</p> + * <pre class="prettyprint"> + * h = Math.log(rh / 100.0) + (17.62 * t) / (243.12 + t); + * td = 243.12 * h / (17.62 - h); + * </pre> + * <u>Absolute Humidity</u> + * <p> + * The absolute humidity is the mass of water vapor in a particular volume + * of dry air. The unit is g/m<sup>3</sup>. + * </p> + * <center><pre> + * RH/100%·A·exp(m·t/(T<sub>n</sub>+t)) + * d<sub>v</sub>(t,RH) = 216.7 · ------------------------- + * 273.15 + t + * </pre></center> + * <dl> + * <dt>d<sub>v</sub></dt> <dd>absolute humidity in g/m<sup>3</sup></dd> + * <dt>t</dt> <dd>actual temperature in °C</dd> + * <dt>RH</dt> <dd>actual relative humidity in %</dd> + * <dt>m</dt> <dd>17.62</dd> + * <dt>T<sub>n</sub></dt> <dd>243.12 °C</dd> + * <dt>A</dt> <dd>6.112 hPa</dd> + * </dl> + * <p>for example:</p> + * <pre class="prettyprint"> + * dv = 216.7 * + * (rh / 100.0 * 6.112 * Math.exp(17.62 * t / (243.12 + t)) / (273.15 + t)); + * </pre> * * <h4>{@link android.hardware.Sensor#TYPE_AMBIENT_TEMPERATURE Sensor.TYPE_AMBIENT_TEMPERATURE}: * </h4> diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index a153c0b..b536490 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -69,6 +69,17 @@ public class UsbDeviceConnection { } /** + * Returns the raw USB descriptors for the device. + * This can be used to access descriptors not supported directly + * via the higher level APIs. + * + * @return raw USB descriptors + */ + public byte[] getRawDescriptors() { + return native_get_desc(); + } + + /** * Claims exclusive access to a {@link android.hardware.usb.UsbInterface}. * This must be done before sending or receiving data on any * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface. @@ -160,6 +171,7 @@ public class UsbDeviceConnection { private native boolean native_open(String deviceName, FileDescriptor pfd); private native void native_close(); private native int native_get_fd(); + private native byte[] native_get_desc(); private native boolean native_claim_interface(int interfaceID, boolean force); private native boolean native_release_interface(int interfaceID); private native int native_control_request(int requestType, int request, int value, diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl new file mode 100644 index 0000000..fa6eae5 --- /dev/null +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 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.net; + +/** + * Interface that creates and modifies network policy rules. + * + * {@hide} + */ +interface INetworkPolicyManager { + + void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities); + void onProcessDied(int uid, int pid); + + void setUidPolicy(int uid, int policy); + int getUidPolicy(int uid); + + // TODO: build API to surface stats details for settings UI + +} diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 61acf2b..19894a0 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -68,7 +68,8 @@ public class LinkProperties implements Parcelable { mLinkAddresses = source.getLinkAddresses(); mDnses = source.getDnses(); mRoutes = source.getRoutes(); - mHttpProxy = new ProxyProperties(source.getHttpProxy()); + mHttpProxy = (source.getHttpProxy() == null) ? + null : new ProxyProperties(source.getHttpProxy()); } } diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index bb6ee0f..770f152 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -276,6 +276,21 @@ public class MobileDataStateTracker implements NetworkStateTracker { setDetailedState(DetailedState.CONNECTED, reason, apnName); break; } + } else { + // There was no state change. Check if LinkProperties has been updated. + if (TextUtils.equals(reason, Phone.REASON_LINK_PROPERTIES_CHANGED)) { + mLinkProperties = intent.getParcelableExtra(Phone.DATA_LINK_PROPERTIES_KEY); + if (mLinkProperties == null) { + log("No link property in LINK_PROPERTIES change event."); + mLinkProperties = new LinkProperties(); + } + // Just update reason field in this NetworkInfo + mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason, + mNetworkInfo.getExtraInfo()); + Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, + mNetworkInfo); + msg.sendToTarget(); + } } } else if (intent.getAction(). equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) { diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java new file mode 100644 index 0000000..2312bd9 --- /dev/null +++ b/core/java/android/net/NetworkPolicyManager.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 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.net; + +import android.os.RemoteException; + +/** + * Manager for creating and modifying network policy rules. + * + * {@hide} + */ +public class NetworkPolicyManager { + + /** No specific network policy, use system default. */ + public static final int POLICY_NONE = 0x0; + /** Reject network usage when application in background. */ + public static final int POLICY_REJECT_BACKGROUND = 0x1; + /** Reject network usage on paid network connections. */ + public static final int POLICY_REJECT_PAID = 0x2; + /** Application should conserve data. */ + public static final int POLICY_CONSERVE_DATA = 0x4; + + private INetworkPolicyManager mService; + + public NetworkPolicyManager(INetworkPolicyManager service) { + if (service == null) { + throw new IllegalArgumentException("missing INetworkPolicyManager"); + } + mService = service; + } + + /** + * Set policy flags for specific UID. + * + * @param policy {@link #POLICY_NONE} or combination of + * {@link #POLICY_REJECT_BACKGROUND}, {@link #POLICY_REJECT_PAID}, + * or {@link #POLICY_CONSERVE_DATA}. + */ + public void setUidPolicy(int uid, int policy) { + try { + mService.setUidPolicy(uid, policy); + } catch (RemoteException e) { + } + } + + public int getUidPolicy(int uid) { + try { + return mService.getUidPolicy(uid); + } catch (RemoteException e) { + return POLICY_NONE; + } + } + +} diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index fbe5379..8a678d6 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -38,32 +38,6 @@ public class NetworkUtils { /** Bring the named network interface down. */ public native static int disableInterface(String interfaceName); - /** - * Add a route to the routing table. - * - * @param interfaceName the interface to route through. - * @param dst the network or host to route to. May be IPv4 or IPv6, e.g. - * "0.0.0.0" or "2001:4860::". - * @param prefixLength the prefix length of the route. - * @param gw the gateway to use, e.g., "192.168.251.1". If null, - * indicates a directly-connected route. - */ - public native static int addRoute(String interfaceName, String dst, - int prefixLength, String gw); - - /** Return the gateway address for the default route for the named interface. */ - public static InetAddress getDefaultRoute(String interfaceName) { - int addr = getDefaultRouteNative(interfaceName); - return intToInetAddress(addr); - } - private native static int getDefaultRouteNative(String interfaceName); - - /** Remove host routes that uses the named interface. */ - public native static int removeHostRoutes(String interfaceName); - - /** Remove the default route for the named interface. */ - public native static int removeDefaultRoute(String interfaceName); - /** Reset any sockets that are connected via the named interface. */ public native static int resetConnections(String interfaceName); @@ -160,6 +134,15 @@ public class NetworkUtils { } /** + * Convert a IPv4 netmask integer to a prefix length + * @param netmask as an integer in network byte order + * @return the network prefix length + */ + public static int netmaskIntToPrefixLength(int netmask) { + return Integer.bitCount(netmask); + } + + /** * Create an InetAddress from a string where the string must be a standard * representation of a V4 or V6 address. Avoids doing a DNS lookup on failure * but it will throw an IllegalArgumentException in that case. @@ -173,60 +156,6 @@ public class NetworkUtils { } /** - * Add a default route through the specified gateway. - * @param interfaceName interface on which the route should be added - * @param gw the IP address of the gateway to which the route is desired, - * @return {@code true} on success, {@code false} on failure - */ - public static boolean addDefaultRoute(String interfaceName, InetAddress gw) { - String dstStr; - String gwStr = gw.getHostAddress(); - - if (gw instanceof Inet4Address) { - dstStr = "0.0.0.0"; - } else if (gw instanceof Inet6Address) { - dstStr = "::"; - } else { - Log.w(TAG, "addDefaultRoute failure: address is neither IPv4 nor IPv6" + - "(" + gwStr + ")"); - return false; - } - return addRoute(interfaceName, dstStr, 0, gwStr) == 0; - } - - /** - * Add a host route. - * @param interfaceName interface on which the route should be added - * @param dst the IP address of the host to which the route is desired, - * this should not be null. - * @param gw the IP address of the gateway to which the route is desired, - * if null, indicates a directly-connected route. - * @return {@code true} on success, {@code false} on failure - */ - public static boolean addHostRoute(String interfaceName, InetAddress dst, - InetAddress gw) { - if (dst == null) { - Log.w(TAG, "addHostRoute: dst should not be null"); - return false; - } - - int prefixLength; - String dstStr = dst.getHostAddress(); - String gwStr = (gw != null) ? gw.getHostAddress() : null; - - if (dst instanceof Inet4Address) { - prefixLength = 32; - } else if (dst instanceof Inet6Address) { - prefixLength = 128; - } else { - Log.w(TAG, "addHostRoute failure: address is neither IPv4 nor IPv6" + - "(" + dst + ")"); - return false; - } - return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0; - } - - /** * Get InetAddress masked with prefixLength. Will never return null. * @param IP address which will be masked with specified prefixLength * @param prefixLength the prefixLength used to mask the IP @@ -271,4 +200,25 @@ public class NetworkUtils { return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) || ((left instanceof Inet6Address) && (right instanceof Inet6Address))); } + + /** + * Convert a 32 char hex string into a Inet6Address. + * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be + * made into an Inet6Address + * @param addrHexString a 32 character hex string representing an IPv6 addr + * @return addr an InetAddress representation for the string + */ + public static InetAddress hexToInet6Address(String addrHexString) + throws IllegalArgumentException { + try { + return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s", + addrHexString.substring(0,4), addrHexString.substring(4,8), + addrHexString.substring(8,12), addrHexString.substring(12,16), + addrHexString.substring(16,20), addrHexString.substring(20,24), + addrHexString.substring(24,28), addrHexString.substring(28,32))); + } catch (Exception e) { + Log.e("NetworkUtils", "error in hexToInet6Address(" + addrHexString + "): " + e); + throw new IllegalArgumentException(e); + } + } } diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index 39e708a..8e5ddda 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -46,18 +46,16 @@ public class RouteInfo implements Parcelable { public RouteInfo(LinkAddress destination, InetAddress gateway) { if (destination == null) { - try { - if (gateway != null) { - if (gateway instanceof Inet4Address) { - destination = new LinkAddress(Inet4Address.ANY, 0); - } else { - destination = new LinkAddress(Inet6Address.ANY, 0); - } + if (gateway != null) { + if (gateway instanceof Inet4Address) { + destination = new LinkAddress(Inet4Address.ANY, 0); } else { - // no destination, no gateway. invalid. - throw new RuntimeException("Invalid arguments passed in."); + destination = new LinkAddress(Inet6Address.ANY, 0); } - } catch (Exception e) {} + } else { + // no destination, no gateway. invalid. + throw new RuntimeException("Invalid arguments passed in."); + } } if (gateway == null) { if (destination.getAddress() instanceof Inet4Address) { @@ -76,6 +74,20 @@ public class RouteInfo implements Parcelable { this(null, gateway); } + public static RouteInfo makeHostRoute(InetAddress host) { + return makeHostRoute(host, null); + } + + public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway) { + if (host == null) return null; + + if (host instanceof Inet4Address) { + return new RouteInfo(new LinkAddress(host, 32), gateway); + } else { + return new RouteInfo(new LinkAddress(host, 128), gateway); + } + } + private boolean isDefault() { boolean val = false; if (mGateway != null) { @@ -128,6 +140,33 @@ public class RouteInfo implements Parcelable { } } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (!(obj instanceof RouteInfo)) return false; + + RouteInfo target = (RouteInfo) obj; + + boolean sameDestination = ( mDestination == null) ? + target.getDestination() == null + : mDestination.equals(target.getDestination()); + + boolean sameAddress = (mGateway == null) ? + target.getGateway() == null + : mGateway.equals(target.getGateway()); + + return sameDestination && sameAddress + && mIsDefault == target.mIsDefault; + } + + @Override + public int hashCode() { + return (mDestination == null ? 0 : mDestination.hashCode()) + + (mGateway == null ? 0 :mGateway.hashCode()) + + (mIsDefault ? 3 : 7); + } + public static final Creator<RouteInfo> CREATOR = new Creator<RouteInfo>() { public RouteInfo createFromParcel(Parcel in) { diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index fe36786..ecc111b 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -20,6 +20,7 @@ package android.os; import android.net.InterfaceConfiguration; import android.net.INetworkManagementEventObserver; import android.net.NetworkStats; +import android.net.RouteInfo; import android.net.wifi.WifiConfiguration; /** @@ -58,6 +59,22 @@ interface INetworkManagementService void setInterfaceConfig(String iface, in InterfaceConfiguration cfg); /** + * Retrieves the network routes currently configured on the specified + * interface + */ + RouteInfo[] getRoutes(String iface); + + /** + * Add the specified route to the interface. + */ + void addRoute(String iface, in RouteInfo route); + + /** + * Remove the specified route from the interface. + */ + void removeRoute(String iface, in RouteInfo route); + + /** * Shuts down the service */ void shutdown(); diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index d79f6c8..d68e6fb 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -34,10 +34,10 @@ public class StorageVolume implements Parcelable { private final boolean mRemovable; private final boolean mEmulated; private final int mMtpReserveSpace; + private int mStorageId; public StorageVolume(String path, String description, - boolean removable, boolean emulated, - int mtpReserveSpace) { + boolean removable, boolean emulated, int mtpReserveSpace) { mPath = path; mDescription = description; mRemovable = removable; @@ -45,6 +45,17 @@ public class StorageVolume implements Parcelable { mMtpReserveSpace = mtpReserveSpace; } + // for parcelling only + private StorageVolume(String path, String description, + boolean removable, boolean emulated, int mtpReserveSpace, int storageId) { + mPath = path; + mDescription = description; + mRemovable = removable; + mEmulated = emulated; + mMtpReserveSpace = mtpReserveSpace; + mStorageId = storageId; + } + /** * Returns the mount path for the volume. * @@ -82,6 +93,25 @@ public class StorageVolume implements Parcelable { } /** + * Returns the MTP storage ID for the volume. + * this is also used for the storage_id column in the media provider. + * + * @return MTP storage ID + */ + public int getStorageId() { + return mStorageId; + } + + /** + * Do not call this unless you are MountService + */ + public void setStorageId(int index) { + // storage ID is 0x00010001 for primary storage, + // then 0x00020001, 0x00030001, etc. for secondary storages + mStorageId = ((index + 1) << 16) + 1; + } + + /** * Number of megabytes of space to leave unallocated by MTP. * MTP will subtract this value from the free space it reports back * to the host via GetStorageInfo, and will not allow new files to @@ -123,9 +153,11 @@ public class StorageVolume implements Parcelable { String description = in.readString(); int removable = in.readInt(); int emulated = in.readInt(); + int storageId = in.readInt(); int mtpReserveSpace = in.readInt(); return new StorageVolume(path, description, - removable == 1, emulated == 1, mtpReserveSpace); + removable == 1, emulated == 1, + mtpReserveSpace, storageId); } public StorageVolume[] newArray(int size) { @@ -142,6 +174,7 @@ public class StorageVolume implements Parcelable { parcel.writeString(mDescription); parcel.writeInt(mRemovable ? 1 : 0); parcel.writeInt(mEmulated ? 1 : 0); + parcel.writeInt(mStorageId); parcel.writeInt(mMtpReserveSpace); } } diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java index e6513fd..247e297 100644 --- a/core/java/android/server/BluetoothInputProfileHandler.java +++ b/core/java/android/server/BluetoothInputProfileHandler.java @@ -60,7 +60,7 @@ final class BluetoothInputProfileHandler { return sInstance; } - synchronized boolean connectInputDevice(BluetoothDevice device, + boolean connectInputDevice(BluetoothDevice device, BluetoothDeviceProfileState state) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (objectPath == null || @@ -78,7 +78,7 @@ final class BluetoothInputProfileHandler { return false; } - synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { + boolean connectInputDeviceInternal(BluetoothDevice device) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); if (!mBluetoothService.connectInputDeviceNative(objectPath)) { @@ -88,7 +88,7 @@ final class BluetoothInputProfileHandler { return true; } - synchronized boolean disconnectInputDevice(BluetoothDevice device, + boolean disconnectInputDevice(BluetoothDevice device, BluetoothDeviceProfileState state) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (objectPath == null || @@ -105,7 +105,7 @@ final class BluetoothInputProfileHandler { return false; } - synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { + boolean disconnectInputDeviceInternal(BluetoothDevice device) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) { @@ -115,31 +115,31 @@ final class BluetoothInputProfileHandler { return true; } - synchronized int getInputDeviceConnectionState(BluetoothDevice device) { + int getInputDeviceConnectionState(BluetoothDevice device) { if (mInputDevices.get(device) == null) { return BluetoothInputDevice.STATE_DISCONNECTED; } return mInputDevices.get(device); } - synchronized List<BluetoothDevice> getConnectedInputDevices() { + List<BluetoothDevice> getConnectedInputDevices() { List<BluetoothDevice> devices = lookupInputDevicesMatchingStates( new int[] {BluetoothInputDevice.STATE_CONNECTED}); return devices; } - synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) { + List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) { List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(states); return devices; } - synchronized int getInputDevicePriority(BluetoothDevice device) { + int getInputDevicePriority(BluetoothDevice device) { return Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), BluetoothInputDevice.PRIORITY_UNDEFINED); } - synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { + boolean setInputDevicePriority(BluetoothDevice device, int priority) { if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { return false; } @@ -148,7 +148,7 @@ final class BluetoothInputProfileHandler { priority); } - synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { + List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); for (BluetoothDevice device: mInputDevices.keySet()) { @@ -163,7 +163,7 @@ final class BluetoothInputProfileHandler { return inputDevices; } - private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) { + private void handleInputDeviceStateChange(BluetoothDevice device, int state) { int prevState; if (mInputDevices.get(device) == null) { prevState = BluetoothInputDevice.STATE_DISCONNECTED; @@ -194,7 +194,7 @@ final class BluetoothInputProfileHandler { mBluetoothService.sendConnectionStateChange(device, state, prevState); } - synchronized void handleInputDevicePropertyChange(String address, boolean connected) { + void handleInputDevicePropertyChange(String address, boolean connected) { int state = connected ? BluetoothInputDevice.STATE_CONNECTED : BluetoothInputDevice.STATE_DISCONNECTED; BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -202,7 +202,7 @@ final class BluetoothInputProfileHandler { handleInputDeviceStateChange(device, state); } - synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) { + void setInitialInputDevicePriority(BluetoothDevice device, int state) { switch (state) { case BluetoothDevice.BOND_BONDED: if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) { diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java index 8925856..0d63e19 100644 --- a/core/java/android/server/BluetoothPanProfileHandler.java +++ b/core/java/android/server/BluetoothPanProfileHandler.java @@ -76,17 +76,17 @@ final class BluetoothPanProfileHandler { } } - static synchronized BluetoothPanProfileHandler getInstance(Context context, + static BluetoothPanProfileHandler getInstance(Context context, BluetoothService service) { if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service); return sInstance; } - synchronized boolean isTetheringOn() { + boolean isTetheringOn() { return mTetheringOn; } - synchronized boolean allowIncomingTethering() { + boolean allowIncomingTethering() { if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) return true; return false; @@ -94,7 +94,7 @@ final class BluetoothPanProfileHandler { private BroadcastReceiver mTetheringReceiver = null; - synchronized void setBluetoothTethering(boolean value) { + void setBluetoothTethering(boolean value) { if (!value) { disconnectPanServerDevices(); } @@ -104,7 +104,7 @@ final class BluetoothPanProfileHandler { filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mTetheringReceiver = new BroadcastReceiver() { @Override - public synchronized void onReceive(Context context, Intent intent) { + public void onReceive(Context context, Intent intent) { if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) == BluetoothAdapter.STATE_ON) { mTetheringOn = true; @@ -118,7 +118,7 @@ final class BluetoothPanProfileHandler { } } - synchronized int getPanDeviceConnectionState(BluetoothDevice device) { + int getPanDeviceConnectionState(BluetoothDevice device) { BluetoothPanDevice panDevice = mPanDevices.get(device); if (panDevice == null) { return BluetoothPan.STATE_DISCONNECTED; @@ -126,7 +126,7 @@ final class BluetoothPanProfileHandler { return panDevice.mState; } - synchronized boolean connectPanDevice(BluetoothDevice device) { + boolean connectPanDevice(BluetoothDevice device) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")"); if (getPanDeviceConnectionState(device) != BluetoothPan.STATE_DISCONNECTED) { @@ -158,7 +158,7 @@ final class BluetoothPanProfileHandler { } } - private synchronized boolean disconnectPanServerDevices() { + private boolean disconnectPanServerDevices() { debugLog("disconnect all PAN devices"); for (BluetoothDevice device: mPanDevices.keySet()) { @@ -187,7 +187,7 @@ final class BluetoothPanProfileHandler { return true; } - synchronized List<BluetoothDevice> getConnectedPanDevices() { + List<BluetoothDevice> getConnectedPanDevices() { List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); for (BluetoothDevice device: mPanDevices.keySet()) { @@ -198,7 +198,7 @@ final class BluetoothPanProfileHandler { return devices; } - synchronized List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) { + List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) { List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); for (BluetoothDevice device: mPanDevices.keySet()) { @@ -213,7 +213,7 @@ final class BluetoothPanProfileHandler { return devices; } - synchronized boolean disconnectPanDevice(BluetoothDevice device) { + boolean disconnectPanDevice(BluetoothDevice device) { String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); debugLog("disconnect PAN(" + objectPath + ")"); @@ -249,7 +249,7 @@ final class BluetoothPanProfileHandler { return true; } - synchronized void handlePanDeviceStateChange(BluetoothDevice device, + void handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int role) { int prevState; String ifaceAddr = null; @@ -304,7 +304,7 @@ final class BluetoothPanProfileHandler { mBluetoothService.sendConnectionStateChange(device, state, prevState); } - synchronized void handlePanDeviceStateChange(BluetoothDevice device, + void handlePanDeviceStateChange(BluetoothDevice device, int state, int role) { handlePanDeviceStateChange(device, null, state, role); } @@ -343,7 +343,7 @@ final class BluetoothPanProfileHandler { } // configured when we start tethering - private synchronized String enableTethering(String iface) { + private String enableTethering(String iface) { debugLog("updateTetherState:" + iface); IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index f98d275..60bee9a 100644..100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1104,7 +1104,7 @@ public class BluetoothService extends IBluetooth.Stub { } /*package*/ synchronized boolean setBondState(String address, int state, int reason) { - mBondState.setBondState(address.toUpperCase(), state); + mBondState.setBondState(address.toUpperCase(), state, reason); return true; } @@ -1928,120 +1928,163 @@ public class BluetoothService extends IBluetooth.Stub { } /**** Handlers for PAN Profile ****/ + // TODO: This needs to be converted to a state machine. - public synchronized boolean isTetheringOn() { + public boolean isTetheringOn() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothPanProfileHandler.isTetheringOn(); + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.isTetheringOn(); + } } - /*package*/ synchronized boolean allowIncomingTethering() { - return mBluetoothPanProfileHandler.allowIncomingTethering(); + /*package*/boolean allowIncomingTethering() { + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.allowIncomingTethering(); + } } - public synchronized void setBluetoothTethering(boolean value) { + public void setBluetoothTethering(boolean value) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - mBluetoothPanProfileHandler.setBluetoothTethering(value); + synchronized (mBluetoothPanProfileHandler) { + mBluetoothPanProfileHandler.setBluetoothTethering(value); + } } - public synchronized int getPanDeviceConnectionState(BluetoothDevice device) { + public int getPanDeviceConnectionState(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device); + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device); + } } - public synchronized boolean connectPanDevice(BluetoothDevice device) { + public boolean connectPanDevice(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - return mBluetoothPanProfileHandler.connectPanDevice(device); + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.connectPanDevice(device); + } } - public synchronized List<BluetoothDevice> getConnectedPanDevices() { + public List<BluetoothDevice> getConnectedPanDevices() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothPanProfileHandler.getConnectedPanDevices(); + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.getConnectedPanDevices(); + } } - public synchronized List<BluetoothDevice> getPanDevicesMatchingConnectionStates( + public List<BluetoothDevice> getPanDevicesMatchingConnectionStates( int[] states) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states); + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states); + } } - public synchronized boolean disconnectPanDevice(BluetoothDevice device) { + public boolean disconnectPanDevice(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - return mBluetoothPanProfileHandler.disconnectPanDevice(device); + synchronized (mBluetoothPanProfileHandler) { + return mBluetoothPanProfileHandler.disconnectPanDevice(device); + } } - /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + /*package*/void handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int role) { - mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role); + synchronized (mBluetoothPanProfileHandler) { + mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role); + } } - /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + /*package*/void handlePanDeviceStateChange(BluetoothDevice device, int state, int role) { - mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role); + synchronized (mBluetoothPanProfileHandler) { + mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role); + } } /**** Handlers for Input Device Profile ****/ + // This needs to be converted to state machine - public synchronized boolean connectInputDevice(BluetoothDevice device) { + public boolean connectInputDevice(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); - return mBluetoothInputProfileHandler.connectInputDevice(device, state); + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.connectInputDevice(device, state); + } } - public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { - return mBluetoothInputProfileHandler.connectInputDeviceInternal(device); + public boolean connectInputDeviceInternal(BluetoothDevice device) { + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.connectInputDeviceInternal(device); + } } - public synchronized boolean disconnectInputDevice(BluetoothDevice device) { + public boolean disconnectInputDevice(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); - return mBluetoothInputProfileHandler.disconnectInputDevice(device, state); + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.disconnectInputDevice(device, state); + } } - public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { - return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device); + public boolean disconnectInputDeviceInternal(BluetoothDevice device) { + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device); + } } - public synchronized int getInputDeviceConnectionState(BluetoothDevice device) { + public int getInputDeviceConnectionState(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device); - + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device); + } } - public synchronized List<BluetoothDevice> getConnectedInputDevices() { + public List<BluetoothDevice> getConnectedInputDevices() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothInputProfileHandler.getConnectedInputDevices(); + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.getConnectedInputDevices(); + } } - public synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates( + public List<BluetoothDevice> getInputDevicesMatchingConnectionStates( int[] states) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states); + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states); + } } - public synchronized int getInputDevicePriority(BluetoothDevice device) { + public int getInputDevicePriority(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothInputProfileHandler.getInputDevicePriority(device); + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.getInputDevicePriority(device); + } } - public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { + public boolean setInputDevicePriority(BluetoothDevice device, int priority) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority); + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority); + } } - /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { - return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states); + /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { + synchronized (mBluetoothInputProfileHandler) { + return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states); + } } - /*package*/ synchronized void handleInputDevicePropertyChange(String address, boolean connected) { - mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected); + /*package*/void handleInputDevicePropertyChange(String address, boolean connected) { + synchronized (mBluetoothInputProfileHandler) { + mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected); + } } public boolean connectHeadset(String address) { diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 81346b4..332a0fa 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -16,6 +16,10 @@ package android.view; +import android.graphics.Canvas; +import android.os.Handler; +import android.os.Message; +import android.widget.FrameLayout; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -83,6 +87,7 @@ public abstract class LayoutInflater { private static final String TAG_MERGE = "merge"; private static final String TAG_INCLUDE = "include"; + private static final String TAG_1995 = "blink"; private static final String TAG_REQUEST_FOCUS = "requestFocus"; /** @@ -454,7 +459,12 @@ public abstract class LayoutInflater { rInflate(parser, root, attrs, false); } else { // Temp is the root view that was found in the xml - View temp = createViewFromTag(root, name, attrs); + View temp; + if (TAG_1995.equals(name)) { + temp = new BlinkLayout(mContext, attrs); + } else { + temp = createViewFromTag(root, name, attrs); + } ViewGroup.LayoutParams params = null; @@ -605,10 +615,9 @@ public abstract class LayoutInflater { * Throw an exception because the specified class is not allowed to be inflated. */ private void failNotAllowed(String name, String prefix, AttributeSet attrs) { - InflateException ie = new InflateException(attrs.getPositionDescription() + throw new InflateException(attrs.getPositionDescription() + ": Class not allowed to be inflated " + (prefix != null ? (prefix + name) : name)); - throw ie; } /** @@ -720,6 +729,12 @@ public abstract class LayoutInflater { parseInclude(parser, parent, attrs); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); + } else if (TAG_1995.equals(name)) { + final View view = new BlinkLayout(mContext, attrs); + final ViewGroup viewGroup = (ViewGroup) parent; + final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); + rInflate(parser, view, attrs, true); + viewGroup.addView(view, params); } else { final View view = createViewFromTag(parent, name, attrs); final ViewGroup viewGroup = (ViewGroup) parent; @@ -847,5 +862,64 @@ public abstract class LayoutInflater { parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } - } + } + + private static class BlinkLayout extends FrameLayout { + private static final int MESSAGE_BLINK = 0x42; + private static final int BLINK_DELAY = 500; + + private boolean mBlink; + private boolean mBlinkState; + private final Handler mHandler; + + public BlinkLayout(Context context, AttributeSet attrs) { + super(context, attrs); + mHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == MESSAGE_BLINK) { + if (mBlink) { + mBlinkState = !mBlinkState; + makeBlink(); + } + invalidate(); + return true; + } + return false; + } + }); + } + + private void makeBlink() { + Message message = mHandler.obtainMessage(MESSAGE_BLINK); + mHandler.sendMessageDelayed(message, BLINK_DELAY); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + mBlink = true; + mBlinkState = true; + + makeBlink(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + mBlink = false; + mBlinkState = true; + + mHandler.removeMessages(MESSAGE_BLINK); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (mBlinkState) { + super.dispatchDraw(canvas); + } + } + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ecb6bbb..d5f573c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -49,8 +49,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; -import android.text.format.DateUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pool; @@ -1709,6 +1707,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ static final int DRAG_HOVERED = 0x00000002; + /** + * Indicates whether the view is drawn in right-to-left direction. + * + * @hide + */ + static final int RESOLVED_LAYOUT_RTL = 0x00000004; + /* End of masks for mPrivateFlags2 */ static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED; @@ -8488,6 +8493,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH; } jumpDrawablesToCurrentState(); + + // We are supposing here that the parent directionality will be resolved before its children + // View horizontalDirection public attribute resolution to an internal var. + // Resolving the layout direction. LTR is set initially. + mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL; + switch (getHorizontalDirection()) { + case HORIZONTAL_DIRECTION_INHERIT: + // If this is root view, no need to look at parent's layout dir. + if (mParent != null && mParent instanceof ViewGroup && + ((ViewGroup) mParent).isLayoutRtl()) { + mPrivateFlags2 |= RESOLVED_LAYOUT_RTL; + } + break; + case HORIZONTAL_DIRECTION_RTL: + mPrivateFlags2 |= RESOLVED_LAYOUT_RTL; + break; + } } /** @@ -8876,6 +8898,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility // Destroy any previous software drawing cache if needed switch (mLayerType) { + case LAYER_TYPE_HARDWARE: + if (mHardwareLayer != null) { + mHardwareLayer.destroy(); + mHardwareLayer = null; + } + // fall through - unaccelerated views may use software layer mechanism instead case LAYER_TYPE_SOFTWARE: if (mDrawingCache != null) { mDrawingCache.recycle(); @@ -8887,12 +8915,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mUnscaledDrawingCache = null; } break; - case LAYER_TYPE_HARDWARE: - if (mHardwareLayer != null) { - mHardwareLayer.destroy(); - mHardwareLayer = null; - } - break; default: break; } @@ -9975,6 +9997,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Indicates whether or not this view's layout is right-to-left. This is resolved from + * layout attribute and/or the inherited value from the parent.</p> + * + * @return true if the layout is right-to-left. + */ + public boolean isLayoutRtl() { + return (mPrivateFlags2 & RESOLVED_LAYOUT_RTL) == RESOLVED_LAYOUT_RTL; + } + + /** * Assign a size and position to a view and all of its * descendants * diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 1534099..4aa8727 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -92,21 +92,6 @@ public class ViewDebug { public static final boolean TRACE_RECYCLER = false; /** - * Enables or disables motion events tracing. Any invoker of - * {@link #trace(View, MotionEvent, MotionEventTraceType)} should first check - * that this value is set to true as not to affect performance. - * - * @hide - */ - public static final boolean TRACE_MOTION_EVENTS = false; - - /** - * The system property of dynamic switch for capturing view information - * when it is set, we dump interested fields and methods for the view on focus - */ - static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview"; - - /** * The system property of dynamic switch for capturing event information * when it is set, we log key events, touch/motion and trackball events */ @@ -170,12 +155,6 @@ public class ViewDebug { @Debug.DebugProperty public static boolean consistencyCheckEnabled = false; - static { - if (false) { - Debug.setFieldsOn(ViewDebug.class, true); - } - } - /** * This annotation can be used to mark fields and methods to be dumped by * the view server. Only non-void methods with no arguments can be annotated @@ -424,21 +403,6 @@ public class ViewDebug { private static String sRecyclerTracePrefix; /** - * Defines the type of motion events trace to output to the motion events traces file. - * - * @hide - */ - public enum MotionEventTraceType { - DISPATCH, - ON_INTERCEPT, - ON_TOUCH - } - - private static BufferedWriter sMotionEventTraces; - private static ViewAncestor sMotionEventRoot; - private static String sMotionEventTracePrefix; - - /** * Returns the number of instanciated Views. * * @return The number of Views instanciated in the current process. @@ -574,6 +538,7 @@ public class ViewDebug { recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces"); try { if (recyclerDump.exists()) { + //noinspection ResultOfMethodCallIgnored recyclerDump.delete(); } final FileOutputStream file = new FileOutputStream(recyclerDump); @@ -732,146 +697,6 @@ public class ViewDebug { sHierarhcyRoot = null; } - /** - * Outputs a trace to the currently opened traces file. The trace contains the class name - * and instance's hashcode of the specified view as well as the supplied trace type. - * - * @param view the view to trace - * @param event the event of the trace - * @param type the type of the trace - * - * @hide - */ - public static void trace(View view, MotionEvent event, MotionEventTraceType type) { - if (sMotionEventTraces == null) { - return; - } - - try { - sMotionEventTraces.write(type.name()); - sMotionEventTraces.write(' '); - sMotionEventTraces.write(event.getAction()); - sMotionEventTraces.write(' '); - sMotionEventTraces.write(view.getClass().getName()); - sMotionEventTraces.write('@'); - sMotionEventTraces.write(Integer.toHexString(view.hashCode())); - sHierarchyTraces.newLine(); - } catch (IOException e) { - Log.w("View", "Error while dumping trace of event " + event + " for view " + view); - } - } - - /** - * Starts tracing the motion events for the hierarchy of the specificy view. - * The trace is identified by a prefix, used to build the traces files names: - * <code>/EXTERNAL/motion-events/PREFIX.traces</code> and - * <code>/EXTERNAL/motion-events/PREFIX.tree</code>. - * - * Only one view hierarchy can be traced at the same time. After calling this method, any - * other invocation will result in a <code>IllegalStateException</code> unless - * {@link #stopMotionEventTracing()} is invoked before. - * - * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.traces</code> - * containing all the traces (or method calls) relative to the specified view's hierarchy. - * - * This method will return immediately if TRACE_HIERARCHY is false. - * - * @param prefix the traces files name prefix - * @param view the view whose hierarchy must be traced - * - * @see #stopMotionEventTracing() - * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType) - * - * @hide - */ - public static void startMotionEventTracing(String prefix, View view) { - //noinspection PointlessBooleanExpression,ConstantConditions - if (!TRACE_MOTION_EVENTS) { - return; - } - - if (sMotionEventRoot != null) { - throw new IllegalStateException("You must call stopMotionEventTracing() before running" + - " a new trace!"); - } - - File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/"); - //noinspection ResultOfMethodCallIgnored - hierarchyDump.mkdirs(); - - hierarchyDump = new File(hierarchyDump, prefix + ".traces"); - sMotionEventTracePrefix = prefix; - - try { - sMotionEventTraces = new BufferedWriter(new FileWriter(hierarchyDump), 32 * 1024); - } catch (IOException e) { - Log.e("View", "Could not dump view hierarchy"); - return; - } - - sMotionEventRoot = (ViewAncestor) view.getRootView().getParent(); - } - - /** - * Stops the current motion events tracing. This method closes the file - * <code>/EXTERNAL/motion-events/PREFIX.traces</code>. - * - * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.tree</code> - * containing the view hierarchy of the view supplied to - * {@link #startMotionEventTracing(String, View)}. - * - * This method will return immediately if TRACE_HIERARCHY is false. - * - * @see #startMotionEventTracing(String, View) - * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType) - * - * @hide - */ - public static void stopMotionEventTracing() { - //noinspection PointlessBooleanExpression,ConstantConditions - if (!TRACE_MOTION_EVENTS) { - return; - } - - if (sMotionEventRoot == null || sMotionEventTraces == null) { - throw new IllegalStateException("You must call startMotionEventTracing() before" + - " stopMotionEventTracing()!"); - } - - try { - sMotionEventTraces.close(); - } catch (IOException e) { - Log.e("View", "Could not write view traces"); - } - sMotionEventTraces = null; - - File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/"); - //noinspection ResultOfMethodCallIgnored - hierarchyDump.mkdirs(); - hierarchyDump = new File(hierarchyDump, sMotionEventTracePrefix + ".tree"); - - BufferedWriter out; - try { - out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024); - } catch (IOException e) { - Log.e("View", "Could not dump view hierarchy"); - return; - } - - View view = sMotionEventRoot.getView(); - if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup) view; - dumpViewHierarchy(group, out, 0); - try { - out.close(); - } catch (IOException e) { - Log.e("View", "Could not dump view hierarchy"); - } - } - - sHierarhcyRoot = null; - } - static void dispatchCommand(View view, String command, String parameters, OutputStream clientStream) throws IOException { @@ -1069,8 +894,10 @@ public class ViewDebug { try { T[] data = operation.pre(); long start = Debug.threadCpuTimeNanos(); + //noinspection unchecked operation.run(data); duration[0] = Debug.threadCpuTimeNanos() - start; + //noinspection unchecked operation.post(data); } finally { latch.countDown(); @@ -1201,12 +1028,7 @@ public class ViewDebug { cache[0] = captureView.createSnapshot( Bitmap.Config.ARGB_8888, 0, skpiChildren); } catch (OutOfMemoryError e) { - try { - cache[0] = captureView.createSnapshot( - Bitmap.Config.ARGB_4444, 0, skpiChildren); - } catch (OutOfMemoryError e2) { - Log.w("View", "Out of memory for bitmap"); - } + Log.w("View", "Out of memory for bitmap"); } finally { latch.countDown(); } @@ -1316,7 +1138,6 @@ public class ViewDebug { } final HashMap<Class<?>, Field[]> map = sFieldsForClasses; - final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations; Field[] fields = map.get(klass); if (fields != null) { @@ -1332,7 +1153,7 @@ public class ViewDebug { if (field.isAnnotationPresent(ExportedProperty.class)) { field.setAccessible(true); foundFields.add(field); - annotations.put(field, field.getAnnotation(ExportedProperty.class)); + sAnnotations.put(field, field.getAnnotation(ExportedProperty.class)); } } @@ -1351,7 +1172,6 @@ public class ViewDebug { } final HashMap<Class<?>, Method[]> map = sMethodsForClasses; - final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations; Method[] methods = map.get(klass); if (methods != null) { @@ -1369,7 +1189,7 @@ public class ViewDebug { method.getReturnType() != Void.class) { method.setAccessible(true); foundMethods.add(method); - annotations.put(method, method.getAnnotation(ExportedProperty.class)); + sAnnotations.put(method, method.getAnnotation(ExportedProperty.class)); } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 5a418f3..4e52e40 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -466,16 +466,34 @@ public interface WindowManagerPolicy { public int getMaxWallpaperLayer(); /** - * Return the display width available after excluding the window - * decor. + * Return the display width available after excluding any screen + * decorations that can never be removed. That is, system bar or + * button bar. */ - public int getNonDecorDisplayWidth(int fullWidth); + public int getNonDecorDisplayWidth(int rotation, int fullWidth); /** - * Return the display height available after excluding the screen - * decor. + * Return the display height available after excluding any screen + * decorations that can never be removed. That is, system bar or + * button bar. */ - public int getNonDecorDisplayHeight(int fullHeight); + public int getNonDecorDisplayHeight(int rotation, int fullHeight); + + /** + * Return the available screen width that we should report for the + * configuration. This must be no larger than + * {@link #getNonDecorDisplayWidth(int, int)}; it may be smaller than + * that to account for more transient decoration like a status bar. + */ + public int getConfigDisplayWidth(int rotation, int fullWidth); + + /** + * Return the available screen height that we should report for the + * configuration. This must be no larger than + * {@link #getNonDecorDisplayHeight(int, int)}; it may be smaller than + * that to account for more transient decoration like a status bar. + */ + public int getConfigDisplayHeight(int rotation, int fullHeight); /** * Return whether the given window should forcibly hide everything diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index fecf9df..7819b17 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -341,7 +341,7 @@ public class AccessibilityRecord { * * @return An instance. */ - protected static AccessibilityRecord obtain() { + public static AccessibilityRecord obtain() { synchronized (sPoolLock) { if (sPool != null) { AccessibilityRecord record = sPool; diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index f862368..ead9b4f 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -599,12 +599,35 @@ public class ExpandableListView extends ListView { * was already expanded, this will return false) */ public boolean expandGroup(int groupPos) { - boolean retValue = mConnector.expandGroup(groupPos); + return expandGroup(groupPos, false); + } + + /** + * Expand a group in the grouped list view + * + * @param groupPos the group to be expanded + * @param animate true if the expanding group should be animated in + * @return True if the group was expanded, false otherwise (if the group + * was already expanded, this will return false) + */ + public boolean expandGroup(int groupPos, boolean animate) { + PositionMetadata pm = mConnector.getFlattenedPos(ExpandableListPosition.obtain( + ExpandableListPosition.GROUP, groupPos, -1, -1)); + boolean retValue = mConnector.expandGroup(pm); if (mOnGroupExpandListener != null) { mOnGroupExpandListener.onGroupExpand(groupPos); } - + + if (animate) { + final int groupFlatPos = pm.position.flatListPos; + + final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount(); + smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos), + shiftedGroupPosition); + } + pm.recycle(); + return retValue; } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 86fefaf..bac849e 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -1331,7 +1331,7 @@ public class LinearLayout extends ViewGroup { void layoutVertical() { final int paddingLeft = mPaddingLeft; - int childTop = mPaddingTop; + int childTop; int childLeft; // Where right end of child should go @@ -1346,19 +1346,21 @@ public class LinearLayout extends ViewGroup { final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if (majorGravity != Gravity.TOP) { - switch (majorGravity) { - case Gravity.BOTTOM: - // mTotalLength contains the padding already, we add the top - // padding to compensate - childTop = mBottom - mTop + mPaddingTop - mTotalLength; - break; - - case Gravity.CENTER_VERTICAL: - childTop += ((mBottom - mTop) - mTotalLength) / 2; - break; - } - + switch (majorGravity) { + case Gravity.BOTTOM: + // mTotalLength contains the padding already + childTop = mPaddingTop + mBottom - mTop - mTotalLength; + break; + + // mTotalLength contains the padding already + case Gravity.CENTER_VERTICAL: + childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; + break; + + case Gravity.TOP: + default: + childTop = mPaddingTop; + break; } for (int i = 0; i < count; i++) { @@ -1376,12 +1378,8 @@ public class LinearLayout extends ViewGroup { if (gravity < 0) { gravity = minorGravity; } - - switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - childLeft = paddingLeft + lp.leftMargin; - break; + switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp.leftMargin - lp.rightMargin; @@ -1390,11 +1388,13 @@ public class LinearLayout extends ViewGroup { case Gravity.RIGHT: childLeft = childRight - childWidth - lp.rightMargin; break; + + case Gravity.LEFT: default: - childLeft = paddingLeft; + childLeft = paddingLeft + lp.leftMargin; break; } - + if (hasDividerBeforeChildAt(i)) { childTop += mDividerHeight; } @@ -1418,10 +1418,11 @@ public class LinearLayout extends ViewGroup { * @see #onLayout(boolean, int, int, int, int) */ void layoutHorizontal() { + final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; int childTop; - int childLeft = mPaddingLeft; + int childLeft; // Where bottom of child should go final int height = mBottom - mTop; @@ -1440,25 +1441,37 @@ public class LinearLayout extends ViewGroup { final int[] maxAscent = mMaxAscent; final int[] maxDescent = mMaxDescent; - if (majorGravity != Gravity.LEFT) { - switch (majorGravity) { - case Gravity.RIGHT: - // mTotalLength contains the padding already, we add the left - // padding to compensate - childLeft = mRight - mLeft + mPaddingLeft - mTotalLength; - break; - - case Gravity.CENTER_HORIZONTAL: - childLeft += ((mRight - mLeft) - mTotalLength) / 2; - break; - } + switch (majorGravity) { + case Gravity.RIGHT: + // mTotalLength contains the padding already + childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; + break; + + case Gravity.CENTER_HORIZONTAL: + // mTotalLength contains the padding already + childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; + break; + + case Gravity.LEFT: + default: + childLeft = mPaddingLeft; + break; + } + + int start = 0; + int dir = 1; + //In case of RTL, start drawing from the last child. + if (isLayoutRtl) { + start = count - 1; + dir = -1; } for (int i = 0; i < count; i++) { - final View child = getVirtualChildAt(i); + int childIndex = start + dir * i; + final View child = getVirtualChildAt(childIndex); if (child == null) { - childLeft += measureNullChild(i); + childLeft += measureNullChild(childIndex); } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); @@ -1512,7 +1525,7 @@ public class LinearLayout extends ViewGroup { break; } - if (hasDividerBeforeChildAt(i)) { + if (hasDividerBeforeChildAt(childIndex)) { childLeft += mDividerWidth; } @@ -1522,7 +1535,7 @@ public class LinearLayout extends ViewGroup { childLeft += childWidth + lp.rightMargin + getNextLocationOffset(child); - i += getChildrenSkipCount(child, i); + i += getChildrenSkipCount(child, childIndex); } } } |
