diff options
54 files changed, 1373 insertions, 334 deletions
@@ -348,6 +348,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ + media/java/android/media/midi/IBluetoothMidiService.aidl \ media/java/android/media/midi/IMidiDeviceListener.aidl \ media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \ media/java/android/media/midi/IMidiDeviceServer.aidl \ diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0adce5d..7cae745 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1619,7 +1619,8 @@ final class ApplicationPackageManager extends PackageManager { // System apps and apps demanding internal storage can't be moved // anywhere else if (app.isSystemApp() - || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY + || app.installLocation == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { return false; } diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 87c063f..97bd5d2 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -21,7 +21,6 @@ import android.os.Parcel; import android.text.TextUtils; import android.util.Log; -import java.net.InetAddress; import java.net.Inet4Address; import java.util.Objects; @@ -34,7 +33,7 @@ import java.util.Objects; public class DhcpResults extends StaticIpConfiguration { private static final String TAG = "DhcpResults"; - public InetAddress serverAddress; + public Inet4Address serverAddress; /** Vendor specific information (from RFC 2132). */ public String vendorInfo; @@ -142,7 +141,7 @@ public class DhcpResults extends StaticIpConfiguration { private static void readFromParcel(DhcpResults dhcpResults, Parcel in) { StaticIpConfiguration.readFromParcel(dhcpResults, in); dhcpResults.leaseDuration = in.readInt(); - dhcpResults.serverAddress = NetworkUtils.unparcelInetAddress(in); + dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in); dhcpResults.vendorInfo = in.readString(); } @@ -183,8 +182,8 @@ public class DhcpResults extends StaticIpConfiguration { public boolean setServerAddress(String addrString) { try { - serverAddress = NetworkUtils.numericToInetAddress(addrString); - } catch (IllegalArgumentException e) { + serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString); + } catch (IllegalArgumentException|ClassCastException e) { Log.e(TAG, "setServerAddress failed with addrString " + addrString); return true; } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 3f40484..a83e722 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -50,12 +50,13 @@ public class NetworkPolicyManager { public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2; /* RULE_* are not masks and they must be exclusive */ + public static final int RULE_UNKNOWN = -1; /** All network traffic should be allowed. */ - public static final int RULE_ALLOW_ALL = 0x0; + public static final int RULE_ALLOW_ALL = 0; /** Reject traffic on metered networks. */ - public static final int RULE_REJECT_METERED = 0x1; + public static final int RULE_REJECT_METERED = 1; /** Reject traffic on all networks. */ - public static final int RULE_REJECT_ALL = 0x2; + public static final int RULE_REJECT_ALL = 2; public static final int FIREWALL_RULE_DEFAULT = 0; public static final int FIREWALL_RULE_ALLOW = 1; @@ -326,25 +327,4 @@ public class NetworkPolicyManager { // nothing found above; we can apply policy to UID return true; } - - /** {@hide} */ - public static void dumpPolicy(PrintWriter fout, int policy) { - fout.write("["); - if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) { - fout.write("REJECT_METERED_BACKGROUND"); - } - fout.write("]"); - } - - /** {@hide} */ - public static void dumpRules(PrintWriter fout, int rules) { - fout.write("["); - if ((rules & RULE_REJECT_METERED) != 0) { - fout.write("REJECT_METERED"); - } else if ((rules & RULE_REJECT_ALL) != 0) { - fout.write("REJECT_ALL"); - } - fout.write("]"); - } - } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 97b85e2..fdd34f5 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -471,7 +471,7 @@ public final class Debug * </tbody> * </table> */ - public String getMemoryStat(String statName) { + public String getMemoryStat(String statName) { switch(statName) { case "summary.java-heap": return Integer.toString(getSummaryJavaHeap()); @@ -1538,7 +1538,13 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Retrieves information about this processes memory usages. This information is broken down by - * how much is in use by dalivk, the native heap, and everything else. + * how much is in use by dalvik, the native heap, and everything else. + * + * <p><b>Note:</b> this method directly retrieves memory information for the give process + * from low-level data available to it. It may not be able to retrieve information about + * some protected allocations, such as graphics. If you want to be sure you can see + * all information about allocations by the process, use instead + * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])}.</p> */ public static native void getMemoryInfo(MemoryInfo memoryInfo); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 7d48a9a..db68c29 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -447,10 +447,11 @@ public class SurfaceView extends View { final boolean formatChanged = mFormat != mRequestedFormat; final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; + final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height; if (force || creating || formatChanged || sizeChanged || visibleChanged || mLeft != mLocation[0] || mTop != mLocation[1] - || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { + || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) { if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 43c578f..dea004e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2355,7 +2355,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_APPLYING_INSETS * 1 PFLAG3_FITTING_SYSTEM_WINDOWS * 1 PFLAG3_NESTED_SCROLLING_ENABLED - * 1 PFLAG3_ASSIST_BLOCKED + * 1 PFLAG3_SCROLL_INDICATOR_TOP + * 1 PFLAG3_SCROLL_INDICATOR_BOTTOM + * 1 PFLAG3_SCROLL_INDICATOR_LEFT + * 1 PFLAG3_SCROLL_INDICATOR_RIGHT + * 1 PFLAG3_SCROLL_INDICATOR_START + * 1 PFLAG3_SCROLL_INDICATOR_END + * 1 PFLAG3_ASSIST_BLOCKED * |-------|-------|-------|-------| */ @@ -2549,7 +2555,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Indicates that we are allowing {@link ViewStructure} to traverse * into this view.<p> */ - static final int PFLAG3_ASSIST_BLOCKED = 0x100; + static final int PFLAG3_ASSIST_BLOCKED = 0x4000; /** * Always allow a user to over-scroll this view, provided it is a diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2e0bf06..6bbebb7 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import android.animation.ObjectAnimator; +import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -29,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -46,13 +49,18 @@ import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; import android.text.TextUtils; +import android.util.FloatProperty; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; @@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity { private Intent mReferrerFillInIntent; private ChooserListAdapter mChooserListAdapter; + private ChooserRowAdapter mChooserRowAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); @@ -253,7 +262,9 @@ public class ChooserActivity extends ResolverActivity { boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; - adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); + mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); + mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); + adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { listView.setItemsCanFocus(true); } @@ -913,19 +924,103 @@ public class ChooserActivity extends ResolverActivity { } } + static class RowScale { + private static final int DURATION = 400; + + float mScale; + ChooserRowAdapter mAdapter; + private final ObjectAnimator mAnimator; + + public static final FloatProperty<RowScale> PROPERTY = + new FloatProperty<RowScale>("scale") { + @Override + public void setValue(RowScale object, float value) { + object.mScale = value; + object.mAdapter.notifyDataSetChanged(); + } + + @Override + public Float get(RowScale object) { + return object.mScale; + } + }; + + public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) { + mAdapter = adapter; + mScale = from; + if (from == to) { + mAnimator = null; + return; + } + + mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION); + } + + public RowScale setInterpolator(Interpolator interpolator) { + if (mAnimator != null) { + mAnimator.setInterpolator(interpolator); + } + return this; + } + + public float get() { + return mScale; + } + + public void startAnimation() { + if (mAnimator != null) { + mAnimator.start(); + } + } + + public void cancelAnimation() { + if (mAnimator != null) { + mAnimator.cancel(); + } + } + } + class ChooserRowAdapter extends BaseAdapter { private ChooserListAdapter mChooserListAdapter; private final LayoutInflater mLayoutInflater; private final int mColumnCount = 4; + private RowScale[] mServiceTargetScale; + private final Interpolator mInterpolator; public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); + mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this, + android.R.interpolator.decelerate_quint); + wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); + final int rcount = getServiceTargetRowCount(); + if (mServiceTargetScale == null + || mServiceTargetScale.length != rcount) { + RowScale[] old = mServiceTargetScale; + int oldRCount = old != null ? old.length : 0; + mServiceTargetScale = new RowScale[rcount]; + if (old != null && rcount > 0) { + System.arraycopy(old, 0, mServiceTargetScale, 0, + Math.min(old.length, rcount)); + } + + for (int i = rcount; i < oldRCount; i++) { + old[i].cancelAnimation(); + } + + for (int i = oldRCount; i < rcount; i++) { + final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) + .setInterpolator(mInterpolator); + mServiceTargetScale[i] = rs; + rs.startAnimation(); + } + } + notifyDataSetChanged(); } @@ -933,19 +1028,43 @@ public class ChooserActivity extends ResolverActivity { public void onInvalidated() { super.onInvalidated(); notifyDataSetInvalidated(); + if (mServiceTargetScale != null) { + for (RowScale rs : mServiceTargetScale) { + rs.cancelAnimation(); + } + } } }); } + private float getRowScale(int rowPosition) { + final int start = getCallerTargetRowCount(); + final int end = start + getServiceTargetRowCount(); + if (rowPosition >= start && rowPosition < end) { + return mServiceTargetScale[rowPosition - start].get(); + } + return 1.f; + } + @Override public int getCount() { return (int) ( - Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount) - + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount) + getCallerTargetRowCount() + + getServiceTargetRowCount() + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) ); } + public int getCallerTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount); + } + + public int getServiceTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount); + } + @Override public Object getItem(int position) { // We have nothing useful to return here. @@ -959,33 +1078,67 @@ public class ChooserActivity extends ResolverActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { - final View[] holder; + final RowViewHolder holder; if (convertView == null) { holder = createViewHolder(parent); } else { - holder = (View[]) convertView.getTag(); + holder = (RowViewHolder) convertView.getTag(); } bindViewHolder(position, holder); - // We keep the actual list item view as the last item in the holder array - return holder[mColumnCount]; + return holder.row; } - View[] createViewHolder(ViewGroup parent) { - final View[] holder = new View[mColumnCount + 1]; - + RowViewHolder createViewHolder(ViewGroup parent) { final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); + final RowViewHolder holder = new RowViewHolder(row, mColumnCount); + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + for (int i = 0; i < mColumnCount; i++) { - holder[i] = mChooserListAdapter.createView(row); - row.addView(holder[i]); + final View v = mChooserListAdapter.createView(row); + v.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startSelected(holder.itemIndex, false, true); + } + }); + v.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showAppDetails( + mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true)); + return true; + } + }); + row.addView(v); + holder.cells[i] = v; + + // Force height to be a given so we don't have visual disruption during scaling. + LayoutParams lp = v.getLayoutParams(); + v.measure(spec, spec); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); + row.setLayoutParams(lp); + } else { + lp.height = v.getMeasuredHeight(); + } + } + + // Pre-measure so we can scale later. + holder.measure(); + LayoutParams lp = row.getLayoutParams(); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight); + row.setLayoutParams(lp); + } else { + lp.height = holder.measuredRowHeight; } row.setTag(holder); - holder[mColumnCount] = row; return holder; } - void bindViewHolder(int rowPosition, View[] holder) { + void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); @@ -994,34 +1147,26 @@ public class ChooserActivity extends ResolverActivity { end--; } - final ViewGroup row = (ViewGroup) holder[mColumnCount]; - if (startType == ChooserListAdapter.TARGET_SERVICE) { - row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); + holder.row.setBackgroundColor( + getColor(R.color.chooser_service_row_background_color)); } else { - row.setBackground(null); + holder.row.setBackgroundColor(Color.TRANSPARENT); + } + + final int oldHeight = holder.row.getLayoutParams().height; + holder.row.getLayoutParams().height = Math.max(1, + (int) (holder.measuredRowHeight * getRowScale(rowPosition))); + if (holder.row.getLayoutParams().height != oldHeight) { + holder.row.requestLayout(); } for (int i = 0; i < mColumnCount; i++) { - final View v = holder[i]; + final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); - final int itemIndex = start + i; - mChooserListAdapter.bindView(itemIndex, v); - v.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startSelected(itemIndex, false, true); - } - }); - v.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - showAppDetails( - mChooserListAdapter.resolveInfoForPosition(itemIndex, true)); - return true; - } - }); + holder.itemIndex = start + i; + mChooserListAdapter.bindView(holder.itemIndex, v); } else { v.setVisibility(View.GONE); } @@ -1048,6 +1193,24 @@ public class ChooserActivity extends ResolverActivity { } } + static class RowViewHolder { + final View[] cells; + final ViewGroup row; + int measuredRowHeight; + int itemIndex; + + public RowViewHolder(ViewGroup row, int cellCount) { + this.row = row; + this.cells = new View[cellCount]; + } + + public void measure() { + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + row.measure(spec, spec); + measuredRowHeight = row.getMeasuredHeight(); + } + } + static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; private ComponentName mConnectedComponent; @@ -1199,4 +1362,44 @@ public class ChooserActivity extends ResolverActivity { mSelectedTarget = null; } } + + class OffsetDataSetObserver extends DataSetObserver { + private final AbsListView mListView; + private int mCachedViewType = -1; + private View mCachedView; + + public OffsetDataSetObserver(AbsListView listView) { + mListView = listView; + } + + @Override + public void onChanged() { + if (mResolverDrawerLayout == null) { + return; + } + + final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); + int offset = 0; + for (int i = 0; i < chooserTargetRows; i++) { + final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i; + final int vt = mChooserRowAdapter.getItemViewType(pos); + if (vt != mCachedViewType) { + mCachedView = null; + } + final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); + int height = ((RowViewHolder) (v.getTag())).measuredRowHeight; + + offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows); + + if (vt >= 0) { + mCachedViewType = vt; + mCachedView = v; + } else { + mCachedViewType = -1; + } + } + + mResolverDrawerLayout.setCollapsibleHeightReserved(offset); + } + } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 1710489..ba0912a 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -103,6 +103,8 @@ public class ResolverActivity extends Activity { private ResolverComparator mResolverComparator; private PickTargetOptionRequest mPickOptionRequest; + protected ResolverDrawerLayout mResolverDrawerLayout; + private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { @@ -253,6 +255,7 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { rdl.setCollapsed(false); } + mResolverDrawerLayout = rdl; } if (title == null) { @@ -1570,7 +1573,10 @@ public class ResolverActivity extends Activity { private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); - holder.text.setText(info.getDisplayLabel()); + final CharSequence label = info.getDisplayLabel(); + if (!TextUtils.equals(holder.text.getText(), label)) { + holder.text.setText(info.getDisplayLabel()); + } if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(info.getExtendedInfo()); diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 7679624..c4347f8 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup { private int mCollapsibleHeight; private int mUncollapsibleHeight; + /** + * The height in pixels of reserved space added to the top of the collapsed UI; + * e.g. chooser targets + */ + private int mCollapsibleHeightReserved; + private int mTopOffset; private boolean mIsDragging; @@ -153,12 +159,62 @@ public class ResolverDrawerLayout extends ViewGroup { } } + public void setCollapsibleHeightReserved(int heightPixels) { + final int oldReserved = mCollapsibleHeightReserved; + mCollapsibleHeightReserved = heightPixels; + + final int dReserved = mCollapsibleHeightReserved - oldReserved; + if (dReserved != 0 && mIsDragging) { + mLastTouchY -= dReserved; + } + + final int oldCollapsibleHeight = mCollapsibleHeight; + mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); + + if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { + return; + } + + invalidate(); + } + private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } + private boolean isDragging() { + return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL; + } + + private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) { + if (oldCollapsibleHeight == mCollapsibleHeight) { + return false; + } + + if (isLaidOut()) { + final boolean isCollapsedOld = mCollapseOffset != 0; + if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight + && mCollapseOffset == oldCollapsibleHeight)) { + // Stay closed even at the new height. + mCollapseOffset = mCollapsibleHeight; + } else { + mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + } + final boolean isCollapsedNew = mCollapseOffset != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + } else { + // Start out collapsed at first unless we restored state for otherwise + mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; + } + return true; + } + private int getMaxCollapsedHeight() { - return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; + return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + + mCollapsibleHeightReserved; } public void setOnDismissedListener(OnDismissedListener listener) { @@ -676,7 +732,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -688,7 +744,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -697,30 +753,43 @@ public class ResolverDrawerLayout extends ViewGroup { heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; - if (isLaidOut()) { - final boolean isCollapsedOld = mCollapseOffset != 0; - if (oldCollapsibleHeight < mCollapsibleHeight - && mCollapseOffset == oldCollapsibleHeight) { - // Stay closed even at the new height. - mCollapseOffset = mCollapsibleHeight; - } else { - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); - } - final boolean isCollapsedNew = mCollapseOffset != 0; - if (isCollapsedOld != isCollapsedNew) { - notifyViewAccessibilityStateChangedIfNeeded( - AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); - } - } else { - // Start out collapsed at first unless we restored state for otherwise - mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; - } + updateCollapseOffset(oldCollapsibleHeight, !isDragging()); mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; setMeasuredDimension(sourceWidth, heightSize); } + private int getHeightUsed(View child) { + // This method exists because we're taking a fast path at measuring ListViews that + // lets us get away with not doing the more expensive wrap_content measurement which + // imposes double child view measurement costs. If we're looking at a ListView, we can + // check against the lowest child view plus padding and margin instead of the actual + // measured height of the ListView. This lets the ListView hang off the edge when + // all of the content would fit on-screen. + + int heightUsed = child.getMeasuredHeight(); + if (child instanceof AbsListView) { + final AbsListView lv = (AbsListView) child; + final int lvPaddingBottom = lv.getPaddingBottom(); + + int lowest = 0; + for (int i = 0, N = lv.getChildCount(); i < N; i++) { + final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom; + if (bottom > lowest) { + lowest = bottom; + } + } + + if (lowest < heightUsed) { + heightUsed = lowest; + } + } + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + return lp.topMargin + heightUsed + lp.bottomMargin; + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = getWidth(); diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index dcdfb6c..41726fb 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -85,7 +85,7 @@ <ListView android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:id="@+id/resolver_list" android:clipToPadding="false" android:scrollbarStyle="outsideOverlay" diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 1c496f6..0a7ac77 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -70,6 +70,7 @@ android:minLines="2" android:maxLines="2" android:gravity="top|center_horizontal" - android:ellipsize="marquee" /> + android:ellipsize="marquee" + android:visibility="gone" /> </LinearLayout> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 1a45b3a..cf08dea 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2095,6 +2095,10 @@ <enum name="hdpi" value="240" /> <!-- An extra high density screen, approximately 320dpi. --> <enum name="xhdpi" value="320" /> + <!-- An extra extra high density screen, approximately 480dpi. --> + <enum name="xxhdpi" value="480" /> + <!-- An extra extra extra high density screen, approximately 640dpi. --> + <enum name="xxxhdpi" value="640" /> </attr> </declare-styleable> diff --git a/data/keyboards/Vendor_18d1_Product_5018.kcm b/data/keyboards/Vendor_18d1_Product_5018.kcm new file mode 100644 index 0000000..0ca85a2 --- /dev/null +++ b/data/keyboards/Vendor_18d1_Product_5018.kcm @@ -0,0 +1,321 @@ +# Copyright (C) 2015 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. + +# +# Key character map for Google Pixel C Keyboard +# + +type FULL + +### Basic QWERTY keys ### + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' + alt: '\u00e7' + shift+alt: '\u00c7' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + alt: '\u0301' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' + alt: '\u0302' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' + alt: '\u0303' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' + ralt: '[' + ralt+shift: '{' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' + ralt: ']' + ralt+shift: '}' +} + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' + alt: '\u00df' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' + alt: '\u0308' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key 0 { + label: '0' + base: '0' + shift: ')' +} + +key 1 { + label: '1' + base: '1' + shift: '!' + ralt: replace ESCAPE +} + +key 2 { + label: '2' + base: '2' + shift: '@' + ralt: '`' + ralt+shift: '~' +} + +key 3 { + label: '3' + base: '3' + shift: '#' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '^' + alt+shift: '\u0302' +} + +key 7 { + label: '7' + base: '7' + shift: '&' +} + +key 8 { + label: '8' + base: '8' + shift: '*' +} + +key 9 { + label: '9' + base: '9' + shift: '(' +} + +key SPACE { + label: ' ' + base: ' ' + alt, meta: fallback SEARCH + ctrl: fallback LANGUAGE_SWITCH +} + +key ENTER { + label: '\n' + base: '\n' +} + +key TAB { + label: '\t' + base: '\t' +} + +key COMMA { + label: ',' + base: ',' + shift: '<' +} + +key PERIOD { + label: '.' + base: '.' + shift: '>' +} + +key SLASH { + label: '/' + base: '/' + shift: '?' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' + ralt: '\\' + ralt+shift: '|' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: ':' +} + +key APOSTROPHE { + label: '\'' + base: '\'' + shift: '"' +} + +### Non-printing keys ### + +key ESCAPE { + base: fallback BACK + alt, meta: fallback HOME + ctrl: fallback MENU +} diff --git a/data/keyboards/Vendor_18d1_Product_5018.kl b/data/keyboards/Vendor_18d1_Product_5018.kl new file mode 100644 index 0000000..e95ccb5 --- /dev/null +++ b/data/keyboards/Vendor_18d1_Product_5018.kl @@ -0,0 +1,84 @@ +# Copyright (C) 2015 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. + +# +# Key layout for Google Pixel C Keyboard +# + +# Row 1 +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 14 DEL # Backspace + +# Row 2 +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 13 EQUALS +key 28 ENTER + +# Row 3 +key 125 META_LEFT # "Search key" +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE + +# Row 4 +key 42 SHIFT_LEFT +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT + +# Row 5 +key 29 CTRL_LEFT +key 56 ALT_LEFT +key 57 SPACE +key 100 ALT_RIGHT +key 103 DPAD_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 108 DPAD_DOWN diff --git a/docs/html/guide/topics/manifest/compatible-screens-element.jd b/docs/html/guide/topics/manifest/compatible-screens-element.jd index 3606b15..de921d2 100644 --- a/docs/html/guide/topics/manifest/compatible-screens-element.jd +++ b/docs/html/guide/topics/manifest/compatible-screens-element.jd @@ -9,7 +9,7 @@ parent.link=manifest-intro.html <pre> <<a href="#compatible-screens">compatible-screens</a>> <<a href="#screen">screen</a> android:<a href="#screenSize">screenSize</a>=["small" | "normal" | "large" | "xlarge"] - android:<a href="#screenDensity">screenDensity</a>=["ldpi" | "mdpi" | "hdpi" | "xhdpi"] /> + android:<a href="#screenDensity">screenDensity</a>=["ldpi" | "mdpi" | "hdpi" | "xhdpi" | "xxhdpi" | "xxxhdpi"] /> ... </compatible-screens> </pre> @@ -94,11 +94,9 @@ href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple <li>{@code mdpi}</li> <li>{@code hdpi}</li> <li>{@code xhdpi}</li> + <li>{@code xxhdpi}</li> + <li>{@code xxxhdpi}</li> </ul> - <p class="note"><strong>Note:</strong> This attribute currently does not accept - {@code xxhdpi} as a valid value, but you can instead specify {@code 480} - as the value, which is the approximate threshold for xhdpi screens.</p> - <p>For information about the different screen densities, see <a href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple Screens</a>.</p> </dd> @@ -110,8 +108,8 @@ href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple <dt>example</dt> <dd> <p>If your application is compatible with only small and normal screens, regardless -of screen density, then you must specify eight different {@code <screen>} elements, -because each screen size has four different density configurations. You must declare each one of +of screen density, then you must specify twelve different {@code <screen>} elements, +because each screen size has six different density configurations. You must declare each one of these; any combination of size and density that you do <em>not</em> specify is considered a screen configuration with which your application is <em>not</em> compatible. Here's what the manifest entry looks like if your application is compatible with only small and normal screens:</p> @@ -125,11 +123,15 @@ entry looks like if your application is compatible with only small and normal sc <screen android:screenSize="small" android:screenDensity="mdpi" /> <screen android:screenSize="small" android:screenDensity="hdpi" /> <screen android:screenSize="small" android:screenDensity="xhdpi" /> + <screen android:screenSize="small" android:screenDensity="xxhdpi" /> + <screen android:screenSize="small" android:screenDensity="xxxhdpi" /> <!-- all normal size screens --> <screen android:screenSize="normal" android:screenDensity="ldpi" /> <screen android:screenSize="normal" android:screenDensity="mdpi" /> <screen android:screenSize="normal" android:screenDensity="hdpi" /> <screen android:screenSize="normal" android:screenDensity="xhdpi" /> + <screen android:screenSize="normal" android:screenDensity="xxhdpi" /> + <screen android:screenSize="normal" android:screenDensity="xxxhdpi" /> </compatible-screens> <application ... > ... diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java index 2f4d136..1fee587 100644 --- a/media/java/android/media/MediaActionSound.java +++ b/media/java/android/media/MediaActionSound.java @@ -52,7 +52,7 @@ public class MediaActionSound { "/system/media/audio/ui/camera_click.ogg", "/system/media/audio/ui/camera_focus.ogg", "/system/media/audio/ui/VideoRecord.ogg", - "/system/media/audio/ui/VideoRecord.ogg" + "/system/media/audio/ui/VideoStop.ogg" }; private static final String TAG = "MediaActionSound"; diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 13b2878..587d494 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -479,11 +479,6 @@ import java.lang.ref.WeakReference; * <td>{} </p></td> * <td>This method can be called in any state and calling it does not change * the object state. </p></td></tr> - * <tr><td>setPlaybackRate</p></td> - * <td>any </p></td> - * <td>{} </p></td> - * <td>This method can be called in any state and calling it does not change - * the object state. </p></td></tr> * <tr><td>setPlaybackParams</p></td> * <td>any </p></td> * <td>{} </p></td> @@ -2243,10 +2238,14 @@ public class MediaPlayer implements SubtitleController.Listener final InputStream fIs = is; final MediaFormat fFormat = format; - // Ensure all input streams are closed. It is also a handy - // way to implement timeouts in the future. - synchronized(mOpenSubtitleSources) { - mOpenSubtitleSources.add(is); + if (is != null) { + // Ensure all input streams are closed. It is also a handy + // way to implement timeouts in the future. + synchronized(mOpenSubtitleSources) { + mOpenSubtitleSources.add(is); + } + } else { + Log.w(TAG, "addSubtitleSource called with null InputStream"); } // process each subtitle in its own thread diff --git a/media/java/android/media/midi/IBluetoothMidiService.aidl b/media/java/android/media/midi/IBluetoothMidiService.aidl new file mode 100644 index 0000000..fe5566d --- /dev/null +++ b/media/java/android/media/midi/IBluetoothMidiService.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 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.media.midi; + +import android.bluetooth.BluetoothDevice; +import android.os.IBinder; + +/** @hide */ +interface IBluetoothMidiService +{ + IBinder addBluetoothDevice(in BluetoothDevice bluetoothDevice); +} diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java index 7197dc0..266b0d9 100644 --- a/media/java/android/media/midi/MidiManager.java +++ b/media/java/android/media/midi/MidiManager.java @@ -169,6 +169,13 @@ public final class MidiManager { /** * Registers a callback to receive notifications when MIDI devices are added and removed. * + * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately + * for any devices that have open ports. This allows applications to know which input + * ports are already in use and, therefore, unavailable. + * + * Applications should call {@link #getDevices} before registering the callback + * to get a list of devices already added. + * * @param callback a {@link DeviceCallback} for MIDI device notifications * @param handler The {@link android.os.Handler Handler} that will be used for delivering the * device notifications. If handler is null, then the thread used for the @@ -288,7 +295,6 @@ public final class MidiManager { // fetch MidiDeviceInfo from the server MidiDeviceInfo deviceInfo = server.getDeviceInfo(); device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken); - sendOpenDeviceResponse(device, listenerF, handlerF); } catch (RemoteException e) { Log.e(TAG, "remote exception in getDeviceInfo()"); } diff --git a/media/packages/BluetoothMidiService/Android.mk b/media/packages/BluetoothMidiService/Android.mk index 2c9c3c5..0565925 100644 --- a/media/packages/BluetoothMidiService/Android.mk +++ b/media/packages/BluetoothMidiService/Android.mk @@ -3,7 +3,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_SRC_FILES += \ + $(call all-java-files-under,src) LOCAL_PACKAGE_NAME := BluetoothMidiService LOCAL_CERTIFICATE := platform diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml index b0b389a..1cfd55d 100644 --- a/media/packages/BluetoothMidiService/AndroidManifest.xml +++ b/media/packages/BluetoothMidiService/AndroidManifest.xml @@ -8,7 +8,7 @@ <application android:label="@string/app_name"> - <service android:name="BluetoothMidiService" + <service android:name=".BluetoothMidiService" android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE"> <intent-filter> <action android:name="android.media.midi.BluetoothMidiService" /> diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java index e6d59e4..444705c 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java @@ -147,14 +147,22 @@ public final class BluetoothMidiDevice { // switch to receiving notifications after initial characteristic read mBluetoothGatt.setCharacteristicNotification(characteristic, true); + // Use writeType that requests acknowledgement. + // This improves compatibility with various BLE-MIDI devices. + int originalWriteType = characteristic.getWriteType(); + characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( CLIENT_CHARACTERISTIC_CONFIG); if (descriptor != null) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - mBluetoothGatt.writeDescriptor(descriptor); + boolean result = mBluetoothGatt.writeDescriptor(descriptor); + Log.d(TAG, "writeDescriptor returned " + result); } else { Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice); } + + characteristic.setWriteType(originalWriteType); } @Override diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java index fbde2b4..5541f9f 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java @@ -19,6 +19,7 @@ package com.android.bluetoothmidiservice; import android.app.Service; import android.bluetooth.BluetoothDevice; import android.content.Intent; +import android.media.midi.IBluetoothMidiService; import android.media.midi.MidiManager; import android.os.IBinder; import android.util.Log; @@ -34,24 +35,31 @@ public class BluetoothMidiService extends Service { @Override public IBinder onBind(Intent intent) { - if (MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT.equals(intent.getAction())) { - BluetoothDevice bluetoothDevice = (BluetoothDevice)intent.getParcelableExtra("device"); + // Return the interface + return mBinder; + } + + + private final IBluetoothMidiService.Stub mBinder = new IBluetoothMidiService.Stub() { + + public IBinder addBluetoothDevice(BluetoothDevice bluetoothDevice) { + BluetoothMidiDevice device; if (bluetoothDevice == null) { - Log.e(TAG, "no BluetoothDevice in onBind intent"); + Log.e(TAG, "no BluetoothDevice in addBluetoothDevice()"); return null; } - - BluetoothMidiDevice device; synchronized (mDeviceServerMap) { device = mDeviceServerMap.get(bluetoothDevice); if (device == null) { - device = new BluetoothMidiDevice(this, bluetoothDevice, this); + device = new BluetoothMidiDevice(BluetoothMidiService.this, + bluetoothDevice, BluetoothMidiService.this); + mDeviceServerMap.put(bluetoothDevice, device); } } return device.getBinder(); } - return null; - } + + }; void deviceClosed(BluetoothDevice device) { synchronized (mDeviceServerMap) { diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 382b2d0..ff14f94 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -3,6 +3,7 @@ <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" /> <uses-permission android:name="android.permission.REMOVE_TASKS" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:name=".DocumentsApplication" diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java index 506ec58..f270d9e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java @@ -32,6 +32,7 @@ import android.net.Uri; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.provider.DocumentsContract; @@ -65,6 +66,8 @@ public class CopyService extends IntentService { // TODO: Move it to a shared file when more operations are implemented. public static final int FAILURE_COPY = 1; + private PowerManager mPowerManager; + private NotificationManager mNotificationManager; private Notification.Builder mProgressBuilder; @@ -129,10 +132,14 @@ public class CopyService extends IntentService { return; } + final PowerManager.WakeLock wakeLock = mPowerManager + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST); final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK); try { + wakeLock.acquire(); + // Acquire content providers. mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(), srcs.get(0).authority); @@ -151,6 +158,8 @@ public class CopyService extends IntentService { ContentProviderClient.releaseQuietly(mSrcClient); ContentProviderClient.releaseQuietly(mDstClient); + wakeLock.release(); + // Dismiss the ongoing copy notification when the copy is done. mNotificationManager.cancel(mJobId, 0); @@ -179,7 +188,8 @@ public class CopyService extends IntentService { @Override public void onCreate() { super.onCreate(); - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mPowerManager = getSystemService(PowerManager.class); + mNotificationManager = getSystemService(NotificationManager.class); } /** diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index e3ada5a..0b1f95a 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -170,7 +170,7 @@ <string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string> <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string> <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string> - <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuració rà pida."</string> + <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuració rà pida"</string> <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantalla de bloqueig"</string> <string name="accessibility_desc_settings" msgid="3417884241751434521">"Configuració"</string> <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Visió general"</string> @@ -337,7 +337,7 @@ <string name="user_new_user_name" msgid="426540612051178753">"Usuari nou"</string> <string name="guest_nickname" msgid="8059989128963789678">"Convidat"</string> <string name="guest_new_guest" msgid="600537543078847803">"Afegeix un convidat"</string> - <string name="guest_exit_guest" msgid="7187359342030096885">"Suprimeix l\'usuari"</string> + <string name="guest_exit_guest" msgid="7187359342030096885">"Suprimeix el convidat"</string> <string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"Vols suprimir el convidat?"</string> <string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> <string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"Suprimeix"</string> diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml deleted file mode 100644 index 9f04e1f..0000000 --- a/packages/SystemUI/res/values-en/donottranslate.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2015 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> - -<resources> - <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available --> - <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item> - <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available --> - <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item> -</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index a78e571..9cf669e 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -159,7 +159,7 @@ <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"Se obţine GPS."</string> <string name="accessibility_tty_enabled" msgid="4613200365379426561">"TeleTypewriter activat."</string> <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"Vibrare sonerie."</string> - <string name="accessibility_ringer_silent" msgid="9061243307939135383">"Sonerie silenţioasă."</string> + <string name="accessibility_ringer_silent" msgid="9061243307939135383">"Sonerie silențioasă."</string> <!-- no translation found for accessibility_casting (6887382141726543668) --> <skip /> <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Închideți <xliff:g id="APP">%s</xliff:g>."</string> diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml index 30ff704..351a1fd 100644 --- a/packages/SystemUI/res/values/donottranslate.xml +++ b/packages/SystemUI/res/values/donottranslate.xml @@ -20,10 +20,4 @@ <!-- Date format for display: should match the lockscreen in /policy. --> <string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string> - <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available --> - <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item> - - <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available --> - <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item> - </resources> diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index 803a014..728d558 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -21,6 +21,7 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; import android.net.Uri; import android.os.Looper; import android.os.PowerManager; @@ -36,7 +37,7 @@ import java.util.LinkedList; * - whenever audio is played, audio focus is requested, * - whenever audio playback is stopped or the playback completed, audio focus is abandoned. */ -public class NotificationPlayer implements OnCompletionListener { +public class NotificationPlayer implements OnCompletionListener, OnErrorListener { private static final int PLAY = 1; private static final int STOP = 2; private static final boolean mDebug = false; @@ -112,6 +113,7 @@ public class NotificationPlayer implements OnCompletionListener { // done playing. This class should be modified to use a single thread, on which // command are issued, and on which it receives the completion callbacks. player.setOnCompletionListener(NotificationPlayer.this); + player.setOnErrorListener(NotificationPlayer.this); player.start(); if (mPlayer != null) { mPlayer.release(); @@ -245,6 +247,13 @@ public class NotificationPlayer implements OnCompletionListener { } } + public boolean onError(MediaPlayer mp, int what, int extra) { + Log.e(mTag, "error " + what + " (extra=" + extra + ") playing notification"); + // error happened, handle it just like a completion + onCompletion(mp); + return true; + } + private String mTag; private CmdThread mThread; private CreationAndCompletionThread mCompletionThread; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 50d274d..fd84345 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -200,12 +200,12 @@ public class KeyguardIndicationController { switch (mChargingSpeed) { case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_fast_if_translated + ? R.string.keyguard_indication_charging_time_fast : R.string.keyguard_plugged_in_charging_fast; break; case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_slowly_if_translated + ? R.string.keyguard_indication_charging_time_slowly : R.string.keyguard_plugged_in_charging_slowly; break; default: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index f7c3c67..cc30882 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -250,6 +250,7 @@ public class SignalClusterView @Override public void setNoSims(boolean show) { mNoSimsVisible = show && !mBlockMobile; + apply(); } @Override diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index 8b1a032..4440417 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -1557,9 +1557,21 @@ public class RenderScript { nContextDeinitToClient(mContext); mMessageThread.mRun = false; - try { - mMessageThread.join(); - } catch(InterruptedException e) { + + // Wait for mMessageThread to join. Try in a loop, in case this thread gets interrupted + // during the wait. If interrupted, set the "interrupted" status of the current thread. + boolean hasJoined = false, interrupted = false; + while (!hasJoined) { + try { + mMessageThread.join(); + hasJoined = true; + } catch (InterruptedException e) { + interrupted = true; + } + } + if (interrupted) { + Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join"); + Thread.currentThread().interrupt(); } nContextDestroy(); diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index ffc4fd8..2bc48e4 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -611,7 +611,7 @@ nScriptIntrinsicBLAS_Double(JNIEnv *_env, jobject _this, jlong con, jlong id, ji in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -646,7 +646,7 @@ nScriptIntrinsicBLAS_Complex(JNIEnv *_env, jobject _this, jlong con, jlong id, j in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -681,7 +681,7 @@ nScriptIntrinsicBLAS_Z(JNIEnv *_env, jobject _this, jlong con, jlong id, jint fu in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -707,7 +707,7 @@ nScriptIntrinsicBLAS_BNNM(JNIEnv *_env, jobject _this, jlong con, jlong id, jint in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6190a5a..77837b7 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -946,13 +946,13 @@ public class ConnectivityService extends IConnectivityManager.Stub uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); } - if ((uidRules & RULE_REJECT_ALL) != 0 - || (networkCostly && (uidRules & RULE_REJECT_METERED) != 0)) { + if (uidRules == RULE_REJECT_ALL) { return true; + } else if ((uidRules == RULE_REJECT_METERED) && networkCostly) { + return true; + } else { + return false; } - - // no restrictive rules; network is visible - return false; } /** @@ -3724,7 +3724,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized(mRulesLock) { uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); } - if ((uidRules & (RULE_REJECT_METERED | RULE_REJECT_ALL)) != 0) { + if (uidRules != RULE_ALLOW_ALL) { // we could silently fail or we can filter the available nets to only give // them those they have access to. Chose the more useful networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 80fd441..d2d4b7b 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -242,6 +242,14 @@ public class DeviceIdleController extends SystemService if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { int plugged = intent.getIntExtra("plugged", 0); updateChargingLocked(plugged != 0); + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { + removePowerSaveWhitelistAppInternal(ssp); + } + } } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) { synchronized (DeviceIdleController.this) { stepIdleStateLocked(); @@ -912,6 +920,10 @@ public class DeviceIdleController extends SystemService filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(ACTION_STEP_IDLE_STATE); getContext().registerReceiver(mReceiver, filter); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + getContext().registerReceiver(mReceiver, filter); mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray); @@ -924,7 +936,10 @@ public class DeviceIdleController extends SystemService public boolean addPowerSaveWhitelistAppInternal(String name) { synchronized (this) { try { - ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, 0); + ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) { reportPowerSaveWhitelistChangedLocked(); updateWhitelistAppIdsLocked(); @@ -1518,7 +1533,6 @@ public class DeviceIdleController extends SystemService } catch (IOException e) { } } - } private void readConfigFileLocked(XmlPullParser parser) { @@ -1547,7 +1561,10 @@ public class DeviceIdleController extends SystemService String name = parser.getAttributeValue(null, "n"); if (name != null) { try { - ApplicationInfo ai = pm.getApplicationInfo(name, 0); + ApplicationInfo ai = pm.getApplicationInfo(name, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); mPowerSaveWhitelistUserApps.put(ai.packageName, UserHandle.getAppId(ai.uid)); } catch (PackageManager.NameNotFoundException e) { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 4e11070..6d07a57 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -40,6 +40,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.annotation.Nullable; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AlertDialog; @@ -286,8 +287,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean mSystemReady; /** - * Id of the currently selected input method. + * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. + * method. This is to be synchronized with the secure settings keyed with + * {@link Settings.Secure#DEFAULT_INPUT_METHOD}. + * + * <p>This can be transiently {@code null} when the system is re-initializing input method + * settings, e.g., the system locale is just changed.</p> + * + * <p>Note that {@link #mCurId} is used to track which IME is being connected to + * {@link InputMethodManagerService}.</p> + * + * @see #mCurId */ + @Nullable String mCurMethodId; /** @@ -317,9 +329,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub EditorInfo mCurAttribute; /** - * The input method ID of the input method service that we are currently + * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently * connected to or in the process of connecting to. + * + * <p>This can be {@code null} when no input method is connected.</p> + * + * @see #mCurMethodId */ + @Nullable String mCurId; /** @@ -967,7 +984,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub || (newLocale != null && !newLocale.equals(mLastSystemLocale))) { if (!updateOnlyWhenLocaleChanged) { hideCurrentInputLocked(0, null); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } if (DEBUG) { @@ -1523,7 +1539,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub channel.dispose(); } - void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { + void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) { + if (resetCurrentMethodAndClient) { + mCurMethodId = null; + } + if (mVisibleBound) { mContext.unbindService(mVisibleConnection); mVisibleBound = false; @@ -1550,9 +1570,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurId = null; clearCurMethodLocked(); - if (reportToClient && mCurClient != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + if (resetCurrentMethodAndClient) { + unbindCurrentClientLocked(); } } @@ -1903,13 +1922,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } mShortcutInputMethodsAndSubtypes.clear(); } else { // There is no longer an input method set, so stop any current one. - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } // Here is not the perfect place to reset the switching controller. Ideally diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6d91309..bf63931 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3920,7 +3920,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskModeTasks.add(task); if (task.mLockTaskUid == -1) { - task.mLockTaskUid = task.mCallingUid; + task.mLockTaskUid = task.effectiveUid; } if (andResume) { diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index bb4dbc3..835ba17 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -74,6 +74,7 @@ final class ColorFade { // Set to true when the animation context has been fully prepared. private boolean mPrepared; + private boolean mCreatedResources; private int mMode; private final DisplayManagerInternal mDisplayManagerInternal; @@ -169,6 +170,7 @@ final class ColorFade { } // Done. + mCreatedResources = true; mPrepared = true; // Dejanking optimization. @@ -313,18 +315,17 @@ final class ColorFade { } /** - * Dismisses the color fade animation surface and cleans up. + * Dismisses the color fade animation resources. * - * To prevent stray photons from leaking out after the color fade has been - * turned off, it is a good idea to defer dismissing the animation until the - * color fade has been turned back on fully. + * This function destroys the resources that are created for the color fade + * animation but does not clean up the surface. */ - public void dismiss() { + public void dismissResources() { if (DEBUG) { - Slog.d(TAG, "dismiss"); + Slog.d(TAG, "dismissResources"); } - if (mPrepared) { + if (mCreatedResources) { attachEglContext(); try { destroyScreenshotTexture(); @@ -334,8 +335,28 @@ final class ColorFade { } finally { detachEglContext(); } - destroySurface(); + // This is being called with no active context so shouldn't be + // needed but is safer to not change for now. GLES20.glFlush(); + mCreatedResources = false; + } + } + + /** + * Dismisses the color fade animation surface and cleans up. + * + * To prevent stray photons from leaking out after the color fade has been + * turned off, it is a good idea to defer dismissing the animation until the + * color fade has been turned back on fully. + */ + public void dismiss() { + if (DEBUG) { + Slog.d(TAG, "dismiss"); + } + + if (mPrepared) { + dismissResources(); + destroySurface(); mPrepared = false; } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 452378f..7b49530 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -837,6 +837,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mPendingScreenOff && target != Display.STATE_OFF) { setScreenState(Display.STATE_OFF); mPendingScreenOff = false; + mPowerState.dismissColorFadeResources(); } if (target == Display.STATE_ON) { @@ -910,6 +911,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // A black surface is already hiding the contents of the screen. setScreenState(Display.STATE_OFF); mPendingScreenOff = false; + mPowerState.dismissColorFadeResources(); } else if (performScreenOffTransition && mPowerState.prepareColorFade(mContext, mColorFadeFadesConfig ? diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 2eabd32..9862516 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -187,7 +187,7 @@ final class DisplayPowerState { } /** - * Dismisses the electron beam surface. + * Dismisses the color fade surface. */ public void dismissColorFade() { mColorFade.dismiss(); @@ -195,6 +195,13 @@ final class DisplayPowerState { mColorFadeReady = true; } + /** + * Dismisses the color fade resources. + */ + public void dismissColorFadeResources() { + mColorFade.dismissResources(); + } + /** * Sets the level of the electron beam steering current. * diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 5c1878e..88e86e7 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -39,17 +39,17 @@ import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; +import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.NetworkPolicyManager.RULE_UNKNOWN; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; -import static android.net.NetworkPolicyManager.dumpPolicy; -import static android.net.NetworkPolicyManager.dumpRules; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; import static android.net.NetworkTemplate.MATCH_MOBILE_4G; import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; @@ -108,6 +108,7 @@ import android.net.LinkProperties; import android.net.NetworkIdentity; import android.net.NetworkInfo; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkState; import android.net.NetworkTemplate; @@ -138,6 +139,7 @@ import android.text.format.Time; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.DebugUtils; import android.util.Log; import android.util.NtpTrustedTime; import android.util.Pair; @@ -147,8 +149,6 @@ import android.util.SparseIntArray; import android.util.TrustedTime; import android.util.Xml; -import com.android.server.DeviceIdleController; -import com.android.server.EventLogTags; import libcore.io.IoUtils; import com.android.internal.R; @@ -156,6 +156,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.DeviceIdleController; +import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.google.android.collect.Lists; @@ -279,6 +281,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseIntArray mUidPolicy = new SparseIntArray(); /** Currently derived rules for each UID. */ final SparseIntArray mUidRules = new SparseIntArray(); + + final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray(); + final SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -446,14 +452,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // read policy from disk readPolicyLocked(); - if (mRestrictBackground || mRestrictPower || mDeviceIdleMode) { - updateRulesForGlobalChangeLocked(false); - updateNotificationsLocked(); - } else { - // If we are not in any special mode, we just need to make sure the current - // app idle state is updated. - updateRulesForAppIdleLocked(); - } + updateRulesForGlobalChangeLocked(false); + updateNotificationsLocked(); } updateScreenOn(); @@ -1800,7 +1800,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mDeviceIdleMode != enabled) { mDeviceIdleMode = enabled; if (mSystemReady) { - updateRulesForDeviceIdleLocked(); + // Device idle change means we need to rebuild rules for all + // known apps, so do a global refresh. + updateRulesForGlobalChangeLocked(false); } if (enabled) { EventLogTags.writeDeviceIdleOnPhase("net"); @@ -1938,7 +1940,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("UID="); fout.print(uid); fout.print(" policy="); - dumpPolicy(fout, policy); + fout.print(DebugUtils.flagsToString(NetworkPolicyManager.class, "POLICY_", policy)); fout.println(); } fout.decreaseIndent(); @@ -1983,18 +1985,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("UID="); fout.print(uid); - int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); + final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); fout.print(" state="); fout.print(state); fout.print(state <= ActivityManager.PROCESS_STATE_TOP ? " (fg)" : " (bg)"); - fout.print(" rules="); - final int rulesIndex = mUidRules.indexOfKey(uid); - if (rulesIndex < 0) { - fout.print("UNKNOWN"); - } else { - dumpRules(fout, mUidRules.valueAt(rulesIndex)); - } + final int rule = mUidRules.get(uid, RULE_UNKNOWN); + fout.print(" rule="); + fout.print(DebugUtils.valueToString(NetworkPolicyManager.class, "RULE_", rule)); fout.println(); } @@ -2029,7 +2027,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForUidStateChangeLocked(uid, oldUidState, uidState); if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState) != isProcStateAllowedWhileIdle(uidState)) { - updateRulesForDeviceIdleLocked(); + updateRuleForDeviceIdleLocked(uid); } } } @@ -2043,7 +2041,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForUidStateChangeLocked(uid, oldUidState, ActivityManager.PROCESS_STATE_CACHED_EMPTY); if (mDeviceIdleMode) { - updateRulesForDeviceIdleLocked(); + updateRuleForDeviceIdleLocked(uid); } } } @@ -2090,7 +2088,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mDeviceIdleMode) { // sync the whitelists before enable dozable chain. We don't care about the rules if // we are disabling the chain. - SparseIntArray uidRules = new SparseIntArray(); + final SparseIntArray uidRules = mUidFirewallDozableRules; + uidRules.clear(); final List<UserInfo> users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { UserInfo user = users.get(ui); @@ -2114,6 +2113,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules); } + enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode); } @@ -2127,11 +2127,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT); } } + + updateRulesForUidLocked(uid); } void updateRulesForAppIdleLocked() { + final SparseIntArray uidRules = mUidFirewallStandbyRules; + uidRules.clear(); + // Fully update the app idle firewall chain. - SparseIntArray uidRules = new SparseIntArray(); final List<UserInfo> users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { UserInfo user = users.get(ui); @@ -2142,6 +2146,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } + setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules); } @@ -2154,11 +2159,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } else { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); } + + updateRulesForUidLocked(uid); } void updateRulesForAppIdleParoleLocked() { boolean enableChain = !mUsageStats.isAppIdleParoleOn(); enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain); + updateRulesForUidsLocked(mUidFirewallStandbyRules); } /** @@ -2228,6 +2236,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return true; } + void updateRulesForUidsLocked(SparseIntArray uids) { + for (int i = 0; i < uids.size(); i++) { + updateRulesForUidLocked(uids.keyAt(i)); + } + } + /** * Applies network rules to bandwidth and firewall controllers based on uid policy. * @param uid The uid for which to apply the latest policy @@ -2249,8 +2263,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); final boolean uidForeground = isUidForegroundLocked(uid); - // derive active rules based on policy and active state - + // Derive active rules based on policy and active state int appId = UserHandle.getAppId(uid); int uidRules = RULE_ALLOW_ALL; if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) { @@ -2273,20 +2286,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - final int oldRules = mUidRules.get(uid); + // Check dozable state, which is whitelist + if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE) + && mUidFirewallDozableRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) { + uidRules = RULE_REJECT_ALL; + } + + // Check standby state, which is blacklist + if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY) + && mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) { + uidRules = RULE_REJECT_ALL; + } + final int oldRules = mUidRules.get(uid); if (uidRules == RULE_ALLOW_ALL) { mUidRules.delete(uid); } else { mUidRules.put(uid, uidRules); } - // Update bandwidth rules if necessary - final boolean oldRejectMetered = (oldRules & RULE_REJECT_METERED) != 0; - final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0; - if (oldRejectMetered != rejectMetered) { - setUidNetworkRules(uid, rejectMetered); - } + final boolean rejectMetered = (uidRules == RULE_REJECT_METERED); + setUidNetworkRules(uid, rejectMetered); // dispatch changed rule to existing listeners if (oldRules != uidRules) { @@ -2472,6 +2492,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void setUidFirewallRule(int chain, int uid, int rule) { + if (chain == FIREWALL_CHAIN_DOZABLE) { + mUidFirewallDozableRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_STANDBY) { + mUidFirewallStandbyRules.put(uid, rule); + } + try { mNetworkManager.setFirewallUidRule(chain, uid, rule); } catch (IllegalStateException e) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8e6e688..313972b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6083,41 +6083,6 @@ public class PackageManagerService extends IPackageManager.Stub { it.remove(); } } - // Give priority to system apps. - for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { - PackageParser.Package pkg = it.next(); - if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName); - } - sortedPkgs.add(pkg); - it.remove(); - } - } - // Give priority to updated system apps. - for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { - PackageParser.Package pkg = it.next(); - if (pkg.isUpdatedSystemApp()) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName); - } - sortedPkgs.add(pkg); - it.remove(); - } - } - // Give priority to apps that listen for boot complete. - intent = new Intent(Intent.ACTION_BOOT_COMPLETED); - pkgNames = getPackageNamesForIntent(intent); - for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { - PackageParser.Package pkg = it.next(); - if (pkgNames.contains(pkg.packageName)) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Adding boot app " + sortedPkgs.size() + ": " + pkg.packageName); - } - sortedPkgs.add(pkg); - it.remove(); - } - } // Filter out packages that aren't recently used. filterRecentlyUsedApps(pkgs); // Add all remaining apps. @@ -8366,6 +8331,7 @@ public class PackageManagerService extends IPackageManager.Stub { final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); + boolean runtimePermissionsRevoked = false; int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY; boolean changedInstallPermission = false; @@ -8375,6 +8341,17 @@ public class PackageManagerService extends IPackageManager.Stub { if (!ps.isSharedUser()) { origPermissions = new PermissionsState(permissionsState); permissionsState.reset(); + } else { + // We need to know only about runtime permission changes since the + // calling code always writes the install permissions state but + // the runtime ones are written only if changed. The only cases of + // changed runtime permissions here are promotion of an install to + // runtime and revocation of a runtime from a shared user. + changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw( + ps.sharedUser, UserManagerService.getInstance().getUserIds()); + if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) { + runtimePermissionsRevoked = true; + } } } @@ -8590,9 +8567,11 @@ public class PackageManagerService extends IPackageManager.Stub { ps.installPermissionsFixed = true; } - // Persist the runtime permissions state for users with changes. + // Persist the runtime permissions state for users with changes. If permissions + // were revoked because no app in the shared user declares them we have to + // write synchronously to avoid losing runtime permissions state. for (int userId : changedRuntimePermissionUserIds) { - mSettings.writeRuntimePermissionsForUserLPr(userId, false); + mSettings.writeRuntimePermissionsForUserLPr(userId, runtimePermissionsRevoked); } } @@ -12089,6 +12068,66 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) { + // Collect all used permissions in the UID + ArraySet<String> usedPermissions = new ArraySet<>(); + final int packageCount = su.packages.size(); + for (int i = 0; i < packageCount; i++) { + PackageSetting ps = su.packages.valueAt(i); + if (ps.pkg == null) { + continue; + } + final int requestedPermCount = ps.pkg.requestedPermissions.size(); + for (int j = 0; j < requestedPermCount; j++) { + String permission = ps.pkg.requestedPermissions.get(j); + BasePermission bp = mSettings.mPermissions.get(permission); + if (bp != null) { + usedPermissions.add(permission); + } + } + } + + PermissionsState permissionsState = su.getPermissionsState(); + // Prune install permissions + List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); + final int installPermCount = installPermStates.size(); + for (int i = installPermCount - 1; i >= 0; i--) { + PermissionState permissionState = installPermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); + if (bp != null) { + permissionsState.revokeInstallPermission(bp); + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); + } + } + } + + int[] runtimePermissionChangedUserIds = EmptyArray.INT; + + // Prune runtime permissions + for (int userId : allUserIds) { + List<PermissionState> runtimePermStates = permissionsState + .getRuntimePermissionStates(userId); + final int runtimePermCount = runtimePermStates.size(); + for (int i = runtimePermCount - 1; i >= 0; i--) { + PermissionState permissionState = runtimePermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); + runtimePermissionChangedUserIds = ArrayUtils.appendInt( + runtimePermissionChangedUserIds, userId); + } + } + } + } + + return runtimePermissionChangedUserIds; + } + private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e903e4f..71bbdb6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -642,6 +642,7 @@ public class WindowManagerService extends IWindowManager.Stub final InputManagerService mInputManager; final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; + final Display[] mDisplays; // Who is holding the screen on. Session mHoldingScreenOn; @@ -915,8 +916,8 @@ public class WindowManagerService extends IWindowManager.Stub mFxSession = new SurfaceSession(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); - Display[] displays = mDisplayManager.getDisplays(); - for (Display display : displays) { + mDisplays = mDisplayManager.getDisplays(); + for (Display display : mDisplays) { createDisplayContentLocked(display); } @@ -7641,7 +7642,9 @@ public class WindowManagerService extends IWindowManager.Stub } public void displayReady() { - displayReady(Display.DEFAULT_DISPLAY); + for (Display display : mDisplays) { + displayReady(display.getDisplayId()); + } synchronized(mWindowMap) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 3ecfd55..c6d5a7e 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.XmlResourceParser; +import android.media.midi.IBluetoothMidiService; import android.media.midi.IMidiDeviceListener; import android.media.midi.IMidiDeviceOpenCallback; import android.media.midi.IMidiDeviceServer; @@ -394,7 +395,20 @@ public class MidiService extends IMidiManager.Stub { mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { - IMidiDeviceServer server = IMidiDeviceServer.Stub.asInterface(service); + IMidiDeviceServer server = null; + if (mBluetoothDevice != null) { + IBluetoothMidiService mBluetoothMidiService = IBluetoothMidiService.Stub.asInterface(service); + try { + // We need to explicitly add the device in a separate method + // because onBind() is only called once. + IBinder deviceBinder = mBluetoothMidiService.addBluetoothDevice(mBluetoothDevice); + server = IMidiDeviceServer.Stub.asInterface(deviceBinder); + } catch(RemoteException e) { + Log.e(TAG, "Could not call addBluetoothDevice()", e); + } + } else { + server = IMidiDeviceServer.Stub.asInterface(service); + } setDeviceServer(server); } @@ -411,7 +425,6 @@ public class MidiService extends IMidiManager.Stub { intent.setComponent(new ComponentName( MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS)); - intent.putExtra("device", mBluetoothDevice); } else { intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); intent.setComponent( @@ -575,6 +588,8 @@ public class MidiService extends IMidiManager.Stub { Client client = getClient(token); if (client == null) return; client.addListener(listener); + // Let listener know whether any ports are already busy. + updateStickyDeviceStatus(client.mUid, listener); } @Override @@ -584,6 +599,25 @@ public class MidiService extends IMidiManager.Stub { client.removeListener(listener); } + // Inform listener of the status of all known devices. + private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { + synchronized (mDevicesByInfo) { + for (Device device : mDevicesByInfo.values()) { + // ignore private devices that our client cannot access + if (device.isUidAllowed(uid)) { + try { + MidiDeviceStatus status = device.getDeviceStatus(); + if (status != null) { + listener.onDeviceStatusChanged(status); + } + } catch (RemoteException e) { + Log.e(TAG, "remote exception", e); + } + } + } + } + } + private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0]; public MidiDeviceInfo[] getDevices() { diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java index 334f708..df44b11 100644 --- a/services/net/java/android/net/dhcp/DhcpAckPacket.java +++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java @@ -46,7 +46,7 @@ class DhcpAckPacket extends DhcpPacket { return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + - ", gateway " + mGateway + dnsServers + + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime; } @@ -79,7 +79,7 @@ class DhcpAckPacket extends DhcpPacket { } addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buffer, DHCP_ROUTER, mGateway); + addTlv(buffer, DHCP_ROUTER, mGateways); addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName); addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); addTlv(buffer, DHCP_DNS_SERVER, mDnsServers); diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java index e0d2ac1..28cb114 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -299,6 +299,7 @@ public class DhcpClient extends BaseDhcpStateMachine { Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1); Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName); Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1); + Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0); Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT); NetworkUtils.protectFromVpn(mUdpSock); } catch(SocketException|ErrnoException e) { @@ -308,6 +309,16 @@ public class DhcpClient extends BaseDhcpStateMachine { return true; } + private boolean connectUdpSock(Inet4Address to) { + try { + Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER); + return true; + } catch (SocketException|ErrnoException e) { + Log.e(TAG, "Error connecting UDP socket", e); + return false; + } + } + private static void closeQuietly(FileDescriptor fd) { try { IoBridge.closeAndSignalBlockedThreads(fd); @@ -325,7 +336,7 @@ public class DhcpClient extends BaseDhcpStateMachine { try { mNMService.setInterfaceConfig(mIfaceName, ifcg); } catch (RemoteException|IllegalStateException e) { - Log.e(TAG, "Error configuring IP address : " + e); + Log.e(TAG, "Error configuring IP address " + address + ": ", e); return false; } return true; @@ -345,21 +356,22 @@ public class DhcpClient extends BaseDhcpStateMachine { public void run() { maybeLog("Receive thread started"); while (!stopped) { + int length = 0; // Or compiler can't tell it's initialized if a parse error occurs. try { - int length = Os.read(mPacketSock, mPacket, 0, mPacket.length); + length = Os.read(mPacketSock, mPacket, 0, mPacket.length); DhcpPacket packet = null; packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2); - if (packet != null) { - maybeLog("Received packet: " + packet); - sendMessage(CMD_RECEIVED_PACKET, packet); - } else if (PACKET_DBG) { - Log.d(TAG, - "Can't parse packet" + HexDump.dumpHexString(mPacket, 0, length)); - } + maybeLog("Received packet: " + packet); + sendMessage(CMD_RECEIVED_PACKET, packet); } catch (IOException|ErrnoException e) { if (!stopped) { Log.e(TAG, "Read error", e); } + } catch (DhcpPacket.ParseException e) { + Log.e(TAG, "Can't parse packet: " + e.getMessage()); + if (PACKET_DBG) { + Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length)); + } } } maybeLog("Receive thread stopped"); @@ -376,8 +388,10 @@ public class DhcpClient extends BaseDhcpStateMachine { maybeLog("Broadcasting " + description); Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr); } else { - maybeLog("Unicasting " + description + " to " + to.getHostAddress()); - Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER); + // It's safe to call getpeername here, because we only send unicast packets if we + // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter. + maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock)); + Os.write(mUdpSock, buf); } } catch(ErrnoException|IOException e) { Log.e(TAG, "Can't send packet: ", e); @@ -789,6 +803,7 @@ public class DhcpClient extends BaseDhcpStateMachine { transitionTo(mDhcpBoundState); } } else if (packet instanceof DhcpNakPacket) { + // TODO: Wait a while before returning into INIT state. Log.d(TAG, "Received NAK, returning to INIT"); mOffer = null; transitionTo(mDhcpInitState); @@ -806,10 +821,8 @@ public class DhcpClient extends BaseDhcpStateMachine { @Override public void enter() { super.enter(); - if (setIpAddress(mDhcpLease.ipAddress)) { - maybeLog("Configured IP address " + mDhcpLease.ipAddress); - } else { - Log.e(TAG, "Failed to configure IP address " + mDhcpLease.ipAddress); + if (!setIpAddress(mDhcpLease.ipAddress) || + !connectUdpSock((mDhcpLease.serverAddress))) { notifyFailure(); // There's likely no point in going into DhcpInitState here, we'll probably just // repeat the transaction, get the same IP address as before, and fail. diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java index 7ca7100..99154ef 100644 --- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java +++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java @@ -48,7 +48,7 @@ class DhcpOfferPacket extends DhcpPacket { } return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + - dnsServers + ", gateway " + mGateway + + dnsServers + ", gateways " + mGateways + " lease time " + mLeaseTime + ", domain " + mDomainName; } @@ -81,7 +81,7 @@ class DhcpOfferPacket extends DhcpPacket { } addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buffer, DHCP_ROUTER, mGateway); + addTlv(buffer, DHCP_ROUTER, mGateways); addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName); addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); addTlv(buffer, DHCP_DNS_SERVER, mDnsServers); diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index cbf8fc2..8927bfa 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -114,6 +114,11 @@ abstract class DhcpPacket { protected static final int MAX_LENGTH = 1500; /** + * The magic cookie that identifies this as a DHCP packet instead of BOOTP. + */ + private static final int DHCP_MAGIC_COOKIE = 0x63825363; + + /** * DHCP Optional Type: DHCP Subnet Mask */ protected static final byte DHCP_SUBNET_MASK = 1; @@ -123,7 +128,7 @@ abstract class DhcpPacket { * DHCP Optional Type: DHCP Router */ protected static final byte DHCP_ROUTER = 3; - protected Inet4Address mGateway; + protected List <Inet4Address> mGateways; /** * DHCP Optional Type: DHCP DNS Server @@ -403,7 +408,7 @@ abstract class DhcpPacket { (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes + 64 // empty server host name (64 bytes) + 128); // empty boot file name (128 bytes) - buf.putInt(0x63825363); // magic number + buf.putInt(DHCP_MAGIC_COOKIE); // magic number finishPacket(buf); // round up to an even number of octets @@ -668,6 +673,20 @@ abstract class DhcpPacket { return new String(bytes, 0, length, StandardCharsets.US_ASCII); } + private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) { + return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT); + } + + private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) { + return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER); + } + + public static class ParseException extends Exception { + public ParseException(String msg, Object... args) { + super(String.format(msg, args)); + } + } + /** * Creates a concrete DhcpPacket from the supplied ByteBuffer. The * buffer may have an L2 encapsulation (which is the full EthernetII @@ -677,7 +696,7 @@ abstract class DhcpPacket { * A subset of the optional parameters are parsed and are stored * in object fields. */ - public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) + public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException { // bootp parameters int transactionId; @@ -687,8 +706,8 @@ abstract class DhcpPacket { Inet4Address nextIp; Inet4Address relayIp; byte[] clientMac; - List<Inet4Address> dnsServers = new ArrayList<Inet4Address>(); - Inet4Address gateway = null; // aka router + List<Inet4Address> dnsServers = new ArrayList<>(); + List<Inet4Address> gateways = new ArrayList<>(); // aka router Inet4Address serverIdentifier = null; Inet4Address netMask = null; String message = null; @@ -720,7 +739,8 @@ abstract class DhcpPacket { // check to see if we need to parse L2, IP, and UDP encaps if (pktType == ENCAP_L2) { if (packet.remaining() < MIN_PACKET_LENGTH_L2) { - return null; + throw new ParseException("L2 packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_L2); } byte[] l2dst = new byte[6]; @@ -732,18 +752,20 @@ abstract class DhcpPacket { short l2type = packet.getShort(); if (l2type != OsConstants.ETH_P_IP) - return null; + throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x", + l2type, OsConstants.ETH_P_IP); } if (pktType <= ENCAP_L3) { if (packet.remaining() < MIN_PACKET_LENGTH_L3) { - return null; + throw new ParseException("L3 packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_L3); } byte ipTypeAndLength = packet.get(); int ipVersion = (ipTypeAndLength & 0xf0) >> 4; if (ipVersion != 4) { - return null; + throw new ParseException("Invalid IP version %d", ipVersion); } // System.out.println("ipType is " + ipType); @@ -759,8 +781,9 @@ abstract class DhcpPacket { ipSrc = readIpAddress(packet); ipDst = readIpAddress(packet); - if (ipProto != IP_TYPE_UDP) // UDP - return null; + if (ipProto != IP_TYPE_UDP) { + throw new ParseException("Protocol not UDP: %d", ipProto); + } // Skip options. This cannot cause us to read beyond the end of the buffer because the // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than @@ -776,13 +799,19 @@ abstract class DhcpPacket { short udpLen = packet.getShort(); short udpChkSum = packet.getShort(); - if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT)) + // Only accept packets to or from the well-known client port (expressly permitting + // packets from ports other than the well-known server port; http://b/24687559), and + // server-to-server packets, e.g. for relays. + if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) && + !isPacketServerToServer(udpSrcPort, udpDstPort)) { return null; + } } // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length. if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) { - return null; + throw new ParseException("Invalid type or BOOTP packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_BOOTP); } byte type = packet.get(); @@ -805,7 +834,7 @@ abstract class DhcpPacket { packet.get(ipv4addr); relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); } catch (UnknownHostException ex) { - return null; + throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr)); } // Some DHCP servers have been known to announce invalid client hardware address values such @@ -828,8 +857,10 @@ abstract class DhcpPacket { int dhcpMagicCookie = packet.getInt(); - if (dhcpMagicCookie != 0x63825363) - return null; + if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) { + throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie, + DHCP_MAGIC_COOKIE); + } // parse options boolean notFinishedOptions = true; @@ -852,8 +883,9 @@ abstract class DhcpPacket { expectedLen = 4; break; case DHCP_ROUTER: - gateway = readIpAddress(packet); - expectedLen = 4; + for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { + gateways.add(readIpAddress(packet)); + } break; case DHCP_DNS_SERVER: for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { @@ -937,18 +969,20 @@ abstract class DhcpPacket { } if (expectedLen != optionLen) { - return null; + throw new ParseException("Invalid length %d for option %d, expected %d", + optionLen, optionType, expectedLen); } } } catch (BufferUnderflowException e) { - return null; + throw new ParseException("BufferUnderflowException"); } } DhcpPacket newPacket; switch(dhcpType) { - case -1: return null; + case (byte) 0xFF: + throw new ParseException("No DHCP message type option"); case DHCP_MESSAGE_TYPE_DISCOVER: newPacket = new DhcpDiscoverPacket( transactionId, secs, clientMac, broadcast); @@ -981,14 +1015,13 @@ abstract class DhcpPacket { clientMac); break; default: - System.out.println("Unimplemented type: " + dhcpType); - return null; + throw new ParseException("Unimplemented DHCP type %d", dhcpType); } newPacket.mBroadcastAddress = bcAddr; newPacket.mDnsServers = dnsServers; newPacket.mDomainName = domainName; - newPacket.mGateway = gateway; + newPacket.mGateways = gateways; newPacket.mHostName = hostName; newPacket.mLeaseTime = leaseTime; newPacket.mMessage = message; @@ -1009,7 +1042,7 @@ abstract class DhcpPacket { * Parse a packet from an array of bytes, stopping at the given length. */ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType) - { + throws ParseException { ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN); return decodeFullPacket(buffer, pktType); } @@ -1044,7 +1077,11 @@ abstract class DhcpPacket { } catch (IllegalArgumentException e) { return null; } - results.gateway = mGateway; + + if (mGateways.size() > 0) { + results.gateway = mGateways.get(0); + } + results.dnsServers.addAll(mDnsServers); results.domains = mDomainName; results.serverAddress = mServerIdentifier; @@ -1086,11 +1123,11 @@ abstract class DhcpPacket { public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, - Inet4Address gateway, List<Inet4Address> dnsServers, + List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac); - pkt.mGateway = gateway; + pkt.mGateways = gateways; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; @@ -1106,11 +1143,11 @@ abstract class DhcpPacket { public static ByteBuffer buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, - Inet4Address gateway, List<Inet4Address> dnsServers, + List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac); - pkt.mGateway = gateway; + pkt.mGateways = gateways; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java index cd3b8bb..7e60bf1 100644 --- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java +++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java @@ -117,7 +117,7 @@ public class DhcpPacketTest extends TestCase { private void assertDomainAndVendorInfoParses( String expectedDomain, byte[] domainBytes, - String expectedVendorInfo, byte[] vendorInfoBytes) { + String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception { ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER) .setDomainBytes(domainBytes) .setVendorInfoBytes(vendorInfoBytes) @@ -158,17 +158,25 @@ public class DhcpPacketTest extends TestCase { } private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime, - long leaseTimeMillis, byte[] leaseTimeBytes) { + long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception { TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER); if (leaseTimeBytes != null) { testPacket.setLeaseTimeBytes(leaseTimeBytes); } ByteBuffer packet = testPacket.build(); - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + DhcpPacket offerPacket = null; + if (!expectValid) { - assertNull(offerPacket); + try { + offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + fail("Invalid packet parsed successfully: " + offerPacket); + } catch (ParseException expected) { + } return; } + + offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + assertNotNull(offerPacket); assertEquals(rawLeaseTime, offerPacket.mLeaseTime); DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash. assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis()); @@ -200,14 +208,14 @@ public class DhcpPacketTest extends TestCase { } private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) { + byte[] netmaskBytes) throws Exception { checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes); checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes); } private void checkIpAddress(String expected, byte type, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) { + byte[] netmaskBytes) throws Exception { ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp) .setNetmaskBytes(netmaskBytes) .build(); @@ -506,4 +514,74 @@ public class DhcpPacketTest extends TestCase { assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53", "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults); } + + @SmallTest + public void testUdpServerAnySourcePort() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // Ethernet header. + "9cd917000000001c2e0000000800" + + // IP header. + "45a00148000040003d115087d18194fb0a0f7af2" + + // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). + // NOTE: The server source port is not the canonical port 67. + "C29F004401341268" + + // BOOTP header. + "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" + + // MAC address. + "9cd91700000000000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options. + "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + + "d18180060f0777766d2e6564751c040a0fffffff000000" + ).toCharArray(), false)); + + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); + assertTrue(offerPacket instanceof DhcpOfferPacket); + assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac())); + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("10.15.122.242/16", "10.15.200.23", + "209.129.128.3,209.129.148.3,209.129.128.6", + "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults); + } + + @SmallTest + public void testMultipleRouters() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // Ethernet header. + "fc3d93000000" + "081735000000" + "0800" + + // IP header. + "45000148c2370000ff117ac2c0a8bd02ffffffff" + + // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). + "0043004401343beb" + + // BOOTP header. + "0201060027f518e20000800000000000c0a8bd310000000000000000" + + // MAC address. + "fc3d9300000000000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options. + "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" + + "0308c0a8bd01ffffff0006080808080808080404ff000000000000" + ).toCharArray(), false)); + + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); + assertTrue(offerPacket instanceof DhcpOfferPacket); + assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac())); + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4", + null, "192.171.189.2", null, 28800, false, dhcpResults); + } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 4146c1c..5ad796f 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -314,6 +314,8 @@ public class UsageStatsService extends SystemService implements mAppIdleParoled = paroled; if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleParoled); if (paroled) { + postParoleEndTimeout(); + } else { mLastAppIdleParoledTime = checkAndGetTimeLocked(); postNextParoleTimeout(); } @@ -404,8 +406,6 @@ public class UsageStatsService extends SystemService implements if (timeSinceLastParole > mAppIdleParoleIntervalMillis) { if (DEBUG) Slog.d(TAG, "Crossed default parole interval"); setAppIdleParoled(true); - // Make sure it ends at some point - postParoleEndTimeout(); } else { if (DEBUG) Slog.d(TAG, "Not long enough to go to parole"); postNextParoleTimeout(); @@ -492,7 +492,6 @@ public class UsageStatsService extends SystemService implements if (!deviceIdle && timeSinceLastParole >= mAppIdleParoleIntervalMillis) { if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false"); - postNextParoleTimeout(); setAppIdleParoled(true); } else if (deviceIdle) { if (DEBUG) Slog.i(TAG, "Device idle, back to prison"); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index a8874d0..a96c164 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -925,7 +925,7 @@ public class VoiceInteractionManagerService extends SystemService { return; } synchronized (this) { - pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n"); + pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)"); pw.println(" mEnableService: " + mEnableService); if (mImpl == null) { pw.println(" (No active implementation)"); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 28520be..30296e1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -36,6 +36,7 @@ import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionService; import android.service.voice.VoiceInteractionServiceInfo; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.view.IWindowManager; @@ -114,9 +115,9 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mAm = ActivityManagerNative.getDefault(); VoiceInteractionServiceInfo info; try { - info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Voice interaction service not found: " + service); + info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); + } catch (RemoteException|PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Voice interaction service not found: " + service, e); mInfo = null; mSessionComponentName = null; mIWindowManager = null; @@ -260,9 +261,18 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne } return; } + pw.print(" mUser="); pw.println(mUser); pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); pw.print(" Session service="); pw.println(mInfo.getSessionService()); + pw.println(" Service info:"); + mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " "); + pw.println(" Application info:"); + mInfo.getServiceInfo().applicationInfo.dump(new PrintWriterPrinter(pw), " "); + pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService()); pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); + pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist()); + pw.print(" Supports launch from keyguard="); + pw.println(mInfo.getSupportsLaunchFromKeyguard()); if (mDisabledShowContext != 0) { pw.print(" mDisabledShowContext="); pw.println(Integer.toHexString(mDisabledShowContext)); |