Merge "Teach NetworkIdentity about roaming."
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 5df2343..2b9c082 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -81,4 +81,13 @@
/* Clears default preferences and permissions for the package */
void clearDefaults(String packageName);
+
+ /* Sets the current primary USB function. */
+ void setPrimaryFunction(String functions);
+
+ /* Sets the default primary USB function. */
+ void setDefaultFunction(String functions);
+
+ /* Sets the file path for USB mass storage backing file. */
+ void setMassStorageBackingFile(String path);
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 5994c98..a828a23 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -22,12 +22,9 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Log;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.HashMap;
/**
@@ -50,7 +47,7 @@
* This is a sticky broadcast for clients that includes USB connected/disconnected state,
* <ul>
* <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
- * <li> {@link #USB_CONFIGURATION} integer containing current USB configuration
+ * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
* currently zero if not configured, one for configured.
* <li> {@link #USB_FUNCTION_MASS_STORAGE} boolean extra indicating whether the
* mass storage function is enabled
@@ -128,12 +125,12 @@
public static final String USB_CONNECTED = "connected";
/**
- * Integer extra containing currently set USB configuration.
+ * Boolean extra indicating whether USB is configured.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*
* {@hide}
*/
- public static final String USB_CONFIGURATION = "configuration";
+ public static final String USB_CONFIGURED = "configured";
/**
* Name of the USB mass storage USB function.
@@ -388,21 +385,14 @@
}
}
- private static File getFunctionEnableFile(String function) {
- return new File("/sys/class/usb_composite/" + function + "/enable");
- }
-
- /**
- * Returns true if the specified USB function is supported by the kernel.
- * Note that a USB function maybe supported but disabled.
- *
- * @param function name of the USB function
- * @return true if the USB function is supported.
- *
- * {@hide}
- */
- public static boolean isFunctionSupported(String function) {
- return getFunctionEnableFile(function).exists();
+ private static boolean propertyContainsFunction(String property, String function) {
+ String functions = SystemProperties.get(property, "");
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
}
/**
@@ -413,30 +403,52 @@
*
* {@hide}
*/
- public static boolean isFunctionEnabled(String function) {
+ public boolean isFunctionEnabled(String function) {
+ return propertyContainsFunction("sys.usb.config", function);
+ }
+
+ /**
+ * Sets the primary USB function.
+ *
+ * @param function name of the USB function
+ *
+ * {@hide}
+ */
+ public void setPrimaryFunction(String function) {
try {
- FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
- boolean enabled = (stream.read() == '1');
- stream.close();
- return enabled;
- } catch (IOException e) {
- return false;
+ mService.setPrimaryFunction(function);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setPrimaryFunction", e);
}
}
/**
- * Enables or disables a USB function.
+ * Sets the default primary USB function.
+ *
+ * @param function name of the USB function
*
* {@hide}
*/
- public static boolean setFunctionEnabled(String function, boolean enable) {
+ public void setDefaultFunction(String function) {
try {
- FileOutputStream stream = new FileOutputStream(getFunctionEnableFile(function));
- stream.write(enable ? '1' : '0');
- stream.close();
- return true;
- } catch (IOException e) {
- return false;
+ mService.setDefaultFunction(function);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setDefaultFunction", e);
+ }
+ }
+
+ /**
+ * Sets the file path for USB mass storage backing file.
+ *
+ * @param path backing file path
+ *
+ * {@hide}
+ */
+ public void setMassStorageBackingFile(String path) {
+ try {
+ mService.setMassStorageBackingFile(path);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setDefaultFunction", e);
}
}
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 164c657..0421205 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -306,6 +306,8 @@
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
*
+ * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
+ *
* @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
* texture is not available or the width <= 0 or the height <= 0
*
@@ -328,6 +330,8 @@
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
*
+ * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
+ *
* @param width The width of the bitmap to create
* @param height The height of the bitmap to create
*
@@ -354,6 +358,8 @@
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
*
+ * <p>If an error occurs, the bitmap is left unchanged.</p>
+ *
* @param bitmap The bitmap to copy the content of the surface texture into,
* cannot be null, all configurations are supported
*
@@ -447,5 +453,6 @@
public void onSurfaceTextureDestroyed(SurfaceTexture surface);
}
- private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, int width, int height);
+ private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
+ int width, int height);
}
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index ba3ae58..afbedaf 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -4479,7 +4479,7 @@
ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
foundViews.clear();
- View root = null;
+ View root;
if (accessibilityViewId != View.NO_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 9d8d361..290f90d 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -96,6 +96,8 @@
private LinearLayout mTitleLayout;
private TextView mTitleView;
private TextView mSubtitleView;
+ private View mTitleUpView;
+
private Spinner mSpinner;
private LinearLayout mListNavLayout;
private ScrollingTabContainerView mTabScrollView;
@@ -152,6 +154,16 @@
}
};
+ private final OnClickListener mUpClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ Context context = getContext();
+ if (context instanceof Activity) {
+ Activity activity = (Activity) context;
+ activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+ }
+ }
+ };
+
public ActionBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -230,15 +242,7 @@
a.recycle();
mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
- mHomeLayout.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- Context context = getContext();
- if (context instanceof Activity) {
- Activity activity = (Activity) context;
- activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
- }
- }
- });
+ mHomeLayout.setOnClickListener(mUpClickListener);
mHomeLayout.setClickable(true);
mHomeLayout.setFocusable(true);
}
@@ -438,7 +442,8 @@
}
if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
- final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE;
+ final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0;
+ final int vis = showHome ? VISIBLE : GONE;
mHomeLayout.setVisibility(vis);
if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
@@ -458,6 +463,14 @@
}
}
+ if ((flagsChanged &
+ (ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) {
+ final boolean homeAsUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ final boolean titleUp = homeAsUp && !showHome;
+ mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE);
+ mTitleLayout.setEnabled(titleUp);
+ }
+
if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
addView(mCustomNavView);
@@ -641,6 +654,9 @@
mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
+ mTitleUpView = (View) mTitleLayout.findViewById(R.id.up);
+
+ mTitleLayout.setOnClickListener(mUpClickListener);
if (mTitleStyleRes != 0) {
mTitleView.setTextAppearance(mContext, mTitleStyleRes);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index fd49ae3..cbb110a 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -618,178 +618,20 @@
}
@Override
- public boolean onTouchEvent(MotionEvent motionEvent) {
+ public boolean onTouchEvent(MotionEvent event) {
if (!mInputEnabled || !isEnabled()) {
return false;
}
- final float x = motionEvent.getX();
- final float y = motionEvent.getY();
- Cell hitCell;
- switch(motionEvent.getAction()) {
+ switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
- resetPattern();
- hitCell = detectAndAddHit(x, y);
- if (hitCell != null && mOnPatternListener != null) {
- mPatternInProgress = true;
- mPatternDisplayMode = DisplayMode.Correct;
- mOnPatternListener.onPatternStart();
- } else if (mOnPatternListener != null) {
- mPatternInProgress = false;
- mOnPatternListener.onPatternCleared();
- }
- if (hitCell != null) {
- final float startX = getCenterXForColumn(hitCell.column);
- final float startY = getCenterYForRow(hitCell.row);
-
- final float widthOffset = mSquareWidth / 2f;
- final float heightOffset = mSquareHeight / 2f;
-
- invalidate((int) (startX - widthOffset), (int) (startY - heightOffset),
- (int) (startX + widthOffset), (int) (startY + heightOffset));
- }
- mInProgressX = x;
- mInProgressY = y;
- if (PROFILE_DRAWING) {
- if (!mDrawingProfilingStarted) {
- Debug.startMethodTracing("LockPatternDrawing");
- mDrawingProfilingStarted = true;
- }
- }
+ handleActionDown(event);
return true;
case MotionEvent.ACTION_UP:
- // report pattern detected
- if (!mPattern.isEmpty() && mOnPatternListener != null) {
- mPatternInProgress = false;
- mOnPatternListener.onPatternDetected(mPattern);
- invalidate();
- }
- if (PROFILE_DRAWING) {
- if (mDrawingProfilingStarted) {
- Debug.stopMethodTracing();
- mDrawingProfilingStarted = false;
- }
- }
+ handleActionUp(event);
return true;
case MotionEvent.ACTION_MOVE:
- final int patternSizePreHitDetect = mPattern.size();
- hitCell = detectAndAddHit(x, y);
- final int patternSize = mPattern.size();
- if (hitCell != null && (mOnPatternListener != null) && (patternSize == 1)) {
- mPatternInProgress = true;
- mOnPatternListener.onPatternStart();
- }
- // note current x and y for rubber banding of in progress
- // patterns
- final float dx = Math.abs(x - mInProgressX);
- final float dy = Math.abs(y - mInProgressY);
- if (dx + dy > mSquareWidth * 0.01f) {
- float oldX = mInProgressX;
- float oldY = mInProgressY;
-
- mInProgressX = x;
- mInProgressY = y;
-
- if (mPatternInProgress && patternSize > 0) {
- final ArrayList<Cell> pattern = mPattern;
- final float radius = mSquareWidth * mDiameterFactor * 0.5f;
-
- final Cell lastCell = pattern.get(patternSize - 1);
-
- float startX = getCenterXForColumn(lastCell.column);
- float startY = getCenterYForRow(lastCell.row);
-
- float left;
- float top;
- float right;
- float bottom;
-
- final Rect invalidateRect = mInvalidate;
-
- if (startX < x) {
- left = startX;
- right = x;
- } else {
- left = x;
- right = startX;
- }
-
- if (startY < y) {
- top = startY;
- bottom = y;
- } else {
- top = y;
- bottom = startY;
- }
-
- // Invalidate between the pattern's last cell and the current location
- invalidateRect.set((int) (left - radius), (int) (top - radius),
- (int) (right + radius), (int) (bottom + radius));
-
- if (startX < oldX) {
- left = startX;
- right = oldX;
- } else {
- left = oldX;
- right = startX;
- }
-
- if (startY < oldY) {
- top = startY;
- bottom = oldY;
- } else {
- top = oldY;
- bottom = startY;
- }
-
- // Invalidate between the pattern's last cell and the previous location
- invalidateRect.union((int) (left - radius), (int) (top - radius),
- (int) (right + radius), (int) (bottom + radius));
-
- // Invalidate between the pattern's new cell and the pattern's previous cell
- if (hitCell != null) {
- startX = getCenterXForColumn(hitCell.column);
- startY = getCenterYForRow(hitCell.row);
-
- if (patternSize >= 2) {
- // (re-using hitcell for old cell)
- hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
- oldX = getCenterXForColumn(hitCell.column);
- oldY = getCenterYForRow(hitCell.row);
-
- if (startX < oldX) {
- left = startX;
- right = oldX;
- } else {
- left = oldX;
- right = startX;
- }
-
- if (startY < oldY) {
- top = startY;
- bottom = oldY;
- } else {
- top = oldY;
- bottom = startY;
- }
- } else {
- left = right = startX;
- top = bottom = startY;
- }
-
- final float widthOffset = mSquareWidth / 2f;
- final float heightOffset = mSquareHeight / 2f;
-
- invalidateRect.set((int) (left - widthOffset),
- (int) (top - heightOffset), (int) (right + widthOffset),
- (int) (bottom + heightOffset));
- }
-
- invalidate(invalidateRect);
- } else {
- invalidate();
- }
- }
+ handleActionMove(event);
return true;
case MotionEvent.ACTION_CANCEL:
resetPattern();
@@ -808,6 +650,181 @@
return false;
}
+ private void handleActionMove(MotionEvent event) {
+ // Handle all recent motion events so we don't skip any cells even when the device
+ // is busy...
+ final int historySize = event.getHistorySize();
+ for (int i = 0; i < historySize + 1; i++) {
+ final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
+ final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
+ final int patternSizePreHitDetect = mPattern.size();
+ Cell hitCell = detectAndAddHit(x, y);
+ final int patternSize = mPattern.size();
+ if (hitCell != null && (mOnPatternListener != null) && (patternSize == 1)) {
+ mPatternInProgress = true;
+ mOnPatternListener.onPatternStart();
+ }
+ // note current x and y for rubber banding of in progress patterns
+ final float dx = Math.abs(x - mInProgressX);
+ final float dy = Math.abs(y - mInProgressY);
+ if (dx + dy > mSquareWidth * 0.01f) {
+ float oldX = mInProgressX;
+ float oldY = mInProgressY;
+
+ mInProgressX = x;
+ mInProgressY = y;
+
+ if (mPatternInProgress && patternSize > 0) {
+ final ArrayList<Cell> pattern = mPattern;
+ final float radius = mSquareWidth * mDiameterFactor * 0.5f;
+
+ final Cell lastCell = pattern.get(patternSize - 1);
+
+ float startX = getCenterXForColumn(lastCell.column);
+ float startY = getCenterYForRow(lastCell.row);
+
+ float left;
+ float top;
+ float right;
+ float bottom;
+
+ final Rect invalidateRect = mInvalidate;
+
+ if (startX < x) {
+ left = startX;
+ right = x;
+ } else {
+ left = x;
+ right = startX;
+ }
+
+ if (startY < y) {
+ top = startY;
+ bottom = y;
+ } else {
+ top = y;
+ bottom = startY;
+ }
+
+ // Invalidate between the pattern's last cell and the current location
+ invalidateRect.set((int) (left - radius), (int) (top - radius),
+ (int) (right + radius), (int) (bottom + radius));
+
+ if (startX < oldX) {
+ left = startX;
+ right = oldX;
+ } else {
+ left = oldX;
+ right = startX;
+ }
+
+ if (startY < oldY) {
+ top = startY;
+ bottom = oldY;
+ } else {
+ top = oldY;
+ bottom = startY;
+ }
+
+ // Invalidate between the pattern's last cell and the previous location
+ invalidateRect.union((int) (left - radius), (int) (top - radius),
+ (int) (right + radius), (int) (bottom + radius));
+
+ // Invalidate between the pattern's new cell and the pattern's previous cell
+ if (hitCell != null) {
+ startX = getCenterXForColumn(hitCell.column);
+ startY = getCenterYForRow(hitCell.row);
+
+ if (patternSize >= 2) {
+ // (re-using hitcell for old cell)
+ hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
+ oldX = getCenterXForColumn(hitCell.column);
+ oldY = getCenterYForRow(hitCell.row);
+
+ if (startX < oldX) {
+ left = startX;
+ right = oldX;
+ } else {
+ left = oldX;
+ right = startX;
+ }
+
+ if (startY < oldY) {
+ top = startY;
+ bottom = oldY;
+ } else {
+ top = oldY;
+ bottom = startY;
+ }
+ } else {
+ left = right = startX;
+ top = bottom = startY;
+ }
+
+ final float widthOffset = mSquareWidth / 2f;
+ final float heightOffset = mSquareHeight / 2f;
+
+ invalidateRect.set((int) (left - widthOffset),
+ (int) (top - heightOffset), (int) (right + widthOffset),
+ (int) (bottom + heightOffset));
+ }
+
+ invalidate(invalidateRect);
+ } else {
+ invalidate();
+ }
+ }
+ }
+ }
+
+ private void handleActionUp(MotionEvent event) {
+ // report pattern detected
+ if (!mPattern.isEmpty() && mOnPatternListener != null) {
+ mPatternInProgress = false;
+ mOnPatternListener.onPatternDetected(mPattern);
+ invalidate();
+ }
+ if (PROFILE_DRAWING) {
+ if (mDrawingProfilingStarted) {
+ Debug.stopMethodTracing();
+ mDrawingProfilingStarted = false;
+ }
+ }
+ }
+
+ private void handleActionDown(MotionEvent event) {
+ resetPattern();
+ final float x = event.getX();
+ final float y = event.getY();
+ final Cell hitCell = detectAndAddHit(x, y);
+ if (hitCell != null && mOnPatternListener != null) {
+ mPatternInProgress = true;
+ mPatternDisplayMode = DisplayMode.Correct;
+ mOnPatternListener.onPatternStart();
+ } else if (mOnPatternListener != null) {
+ mPatternInProgress = false;
+ mOnPatternListener.onPatternCleared();
+ }
+ if (hitCell != null) {
+ final float startX = getCenterXForColumn(hitCell.column);
+ final float startY = getCenterYForRow(hitCell.row);
+
+ final float widthOffset = mSquareWidth / 2f;
+ final float heightOffset = mSquareHeight / 2f;
+
+ invalidate((int) (startX - widthOffset), (int) (startY - heightOffset),
+ (int) (startX + widthOffset), (int) (startY + heightOffset));
+ }
+ mInProgressX = x;
+ mInProgressY = y;
+ if (PROFILE_DRAWING) {
+ if (!mDrawingProfilingStarted) {
+ Debug.startMethodTracing("LockPatternDrawing");
+ mDrawingProfilingStarted = true;
+ }
+ }
+ }
+
private float getCenterXForColumn(int column) {
return mPaddingLeft + column * mSquareWidth + mSquareWidth / 2f;
}
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
index d8b729d..e803b26 100644
--- a/core/res/res/layout/action_bar_title_item.xml
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -17,17 +17,32 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingRight="32dip" >
- <TextView android:id="@+id/action_bar_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end" />
- <TextView android:id="@+id/action_bar_subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone" />
+ android:orientation="horizontal"
+ android:paddingRight="16dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:enabled="false">
+
+ <ImageView android:id="@android:id/up"
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|left"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:orientation="vertical">
+ <TextView android:id="@+id/action_bar_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end" />
+ <TextView android:id="@+id/action_bar_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone" />
+ </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml
index 1a3573e..2185467 100644
--- a/core/res/res/layout/alert_dialog_holo.xml
+++ b/core/res/res/layout/alert_dialog_holo.xml
@@ -82,7 +82,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dip"
- android:paddingRight="16dip" />
+ android:paddingRight="16dip"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"/>
</ScrollView>
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_status_land.xml b/core/res/res/layout/keyguard_screen_status_land.xml
index 259a3af..8a02e1f 100644
--- a/core/res/res/layout/keyguard_screen_status_land.xml
+++ b/core/res/res/layout/keyguard_screen_status_land.xml
@@ -133,16 +133,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <TextView
- android:id="@+id/propertyOf"
- android:lineSpacingExtra="8dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="17sp"
- android:layout_marginTop="20dip"
- android:singleLine="false"
- android:textColor="@color/lockscreen_owner_info"
- android:visibility="gone"
- />
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_status_port.xml b/core/res/res/layout/keyguard_screen_status_port.xml
index 680c073..1e87fb3 100644
--- a/core/res/res/layout/keyguard_screen_status_port.xml
+++ b/core/res/res/layout/keyguard_screen_status_port.xml
@@ -130,16 +130,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <TextView
- android:id="@+id/propertyOf"
- android:lineSpacingExtra="8dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="17sp"
- android:singleLine="false"
- android:visibility="gone"
- android:textColor="@color/lockscreen_owner_info"
- />
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 0132b6c..4ffa3405 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -63,10 +63,8 @@
<LinearLayout
android:orientation="horizontal"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="0dip"
- android:layout_marginLeft="12dip"
android:layout_gravity="right">
<TextView
@@ -114,8 +112,8 @@
<com.android.internal.widget.LockPatternView
android:id="@+id/lockPattern"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="300dip"
+ android:layout_height="300dip"
android:layout_rowWeight="1"
android:layout_marginTop="8dip"
android:layout_marginRight="8dip"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d9e7dac..b5f4084 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2608,6 +2608,11 @@
<!-- USB_STORAGE_ERROR dialog ok button-->
<string name="dlg_ok">OK</string>
+ <!-- USB_PREFERENCES: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title -->
+ <string name="usb_preferences_notification_title">USB connected</string>
+ <!-- See USB_PREFERENCES. This is the message. -->
+ <string name="usb_preferece_notification_message">Select to configure USB file transfer.</string>
+
<!-- External media format dialog strings -->
<!-- This is the label for the activity, and should never be visible to the user. -->
<!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. [CHAR LIMIT=20] -->
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 596781e..e64d8ac 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -74,7 +74,7 @@
struct CacheLogger {
CacheLogger() {
- LOGD("Creating OpenGL renderer caches");
+ INIT_LOGD("Creating OpenGL renderer caches");
}
}; // struct CacheLogger
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 146e789..77e63d7 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -326,12 +326,17 @@
return false;
}
+ SkAutoLockPixels alp(*bitmap);
+
GLuint texture;
GLuint previousFbo;
GLenum format;
GLenum type;
+ GLenum error = GL_NO_ERROR;
+ bool status = false;
+
switch (bitmap->config()) {
case SkBitmap::kA8_Config:
format = GL_ALPHA;
@@ -352,10 +357,18 @@
break;
}
+ float alpha = layer->alpha;
+ SkXfermode::Mode mode = layer->mode;
+
+ layer->mode = SkXfermode::kSrc_Mode;
+ layer->alpha = 255;
+ layer->fbo = fbo;
+
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenTextures(1, &texture);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
@@ -368,39 +381,48 @@
glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
0, format, type, NULL);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
- glBindTexture(GL_TEXTURE_2D, layer->texture);
+ {
+ LayerRenderer renderer(layer);
+ renderer.setViewport(bitmap->width(), bitmap->height());
+ renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
+ bitmap->width(), bitmap->height(), !layer->blend);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
- float alpha = layer->alpha;
- SkXfermode::Mode mode = layer->mode;
+ {
+ Rect bounds;
+ bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
+ renderer.drawTextureLayer(layer, bounds);
- layer->mode = SkXfermode::kSrc_Mode;
- layer->alpha = 255;
- layer->fbo = fbo;
+ glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+ type, bitmap->getPixels());
- LayerRenderer renderer(layer);
- renderer.setViewport(bitmap->width(), bitmap->height());
- renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
- bitmap->width(), bitmap->height(), !layer->blend);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
+ }
- Rect bounds;
- bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
- renderer.drawTextureLayer(layer, bounds);
+ status = true;
+ }
- SkAutoLockPixels alp(*bitmap);
- glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels());
+error:
+#if DEBUG_OPENGL
+ if (error != GL_NO_ERROR) {
+ LOGD("GL error while copying layer into bitmap = 0x%x", error);
+ }
+#endif
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-
layer->mode = mode;
layer->alpha = alpha;
layer->fbo = 0;
glDeleteTextures(1, &texture);
caches.fboCache.put(fbo);
- return true;
+ return status;
}
return false;
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5343a05..88774c6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -151,7 +151,6 @@
mSaveCount = 1;
glViewport(0, 0, mWidth, mHeight);
-
glDisable(GL_DITHER);
glEnable(GL_SCISSOR_TEST);
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index c9e0f6f..4e271c7 100644
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -92,13 +92,18 @@
};
private static final String ID_WHERE = Files.FileColumns._ID + "=?";
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
- private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
- private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+
+ private static final String STORAGE_WHERE = Files.FileColumns.STORAGE_ID + "=?";
+ private static final String FORMAT_WHERE = Files.FileColumns.PARENT + "=?";
+ private static final String PARENT_WHERE = Files.FileColumns.FORMAT + "=?";
+ private static final String STORAGE_FORMAT_WHERE = STORAGE_WHERE + " AND "
+ Files.FileColumns.FORMAT + "=?";
- private static final String PARENT_STORAGE_WHERE = PARENT_WHERE + " AND "
- + Files.FileColumns.STORAGE_ID + "=?";
- private static final String PARENT_STORAGE_FORMAT_WHERE = PARENT_STORAGE_WHERE + " AND "
- + Files.FileColumns.FORMAT + "=?";
+ private static final String STORAGE_PARENT_WHERE = STORAGE_WHERE + " AND "
+ + Files.FileColumns.PARENT + "=?";
+ private static final String FORMAT_PARENT_WHERE = FORMAT_WHERE + " AND "
+ + Files.FileColumns.PARENT + "=?";
+ private static final String STORAGE_FORMAT_PARENT_WHERE = STORAGE_FORMAT_WHERE + " AND "
+ + Files.FileColumns.PARENT + "=?";
private final MediaScanner mMediaScanner;
@@ -249,26 +254,67 @@
}
private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
- if (storageID != 0) {
- if (format != 0) {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_STORAGE_FORMAT_WHERE,
- new String[] { Integer.toString(parent), Integer.toString(storageID),
- Integer.toString(format) }, null);
+ if (storageID == 0xFFFFFFFF) {
+ // query all stores
+ if (format == 0) {
+ // query all formats
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, null, null, null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, PARENT_WHERE,
+ new String[] { Integer.toString(parent) }, null);
} else {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_STORAGE_WHERE, new String[]
- { Integer.toString(parent), Integer.toString(storageID) }, null);
+ // query specific format
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_WHERE,
+ new String[] { Integer.toString(format) }, null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_PARENT_WHERE,
+ new String[] { Integer.toString(format), Integer.toString(parent) }, null);
}
} else {
- if (format != 0) {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_FORMAT_WHERE,
- new String[] { Integer.toString(parent), Integer.toString(format) },
- null);
+ // query specific store
+ if (format == 0) {
+ // query all formats
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_WHERE,
+ new String[] { Integer.toString(storageID) }, null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_PARENT_WHERE,
+ new String[] { Integer.toString(storageID), Integer.toString(parent) },
+ null);
} else {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
+ // query specific format
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_WHERE,
+ new String[] { Integer.toString(storageID), Integer.toString(format) },
+ null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_PARENT_WHERE,
+ new String[] { Integer.toString(storageID),
+ Integer.toString(format),
+ Integer.toString(parent) },
+ null);
}
}
}
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 4a8fd3e..9ec73c4 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -533,12 +533,10 @@
MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
- // 0x00000000 for all objects?
+ // 0x00000000 for all objects
if (!hasStorage(storageID))
return MTP_RESPONSE_INVALID_STORAGE_ID;
- if (parent == 0xFFFFFFFF)
- parent = 0;
MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
mData.putAUInt32(handles);
@@ -552,11 +550,9 @@
MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
- // 0x00000000 for all objects?
+ // 0x00000000 for all objects
if (!hasStorage(storageID))
return MTP_RESPONSE_INVALID_STORAGE_ID;
- if (parent == 0xFFFFFFFF)
- parent = 0;
int count = mDatabase->getNumObjects(storageID, format, parent);
if (count >= 0) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 55f5280..6d8eab6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -25,6 +25,10 @@
android:exported="true"
/>
+ <activity android:name=".usb.UsbPreferenceActivity"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
<activity android:name=".usb.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
diff --git a/packages/SystemUI/res/layout/usb_preference_buttons.xml b/packages/SystemUI/res/layout/usb_preference_buttons.xml
new file mode 100644
index 0000000..babe07e
--- /dev/null
+++ b/packages/SystemUI/res/layout/usb_preference_buttons.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Check box that is displayed in the activity resolver UI for the user
+ to make their selection the preferred activity. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="14dip"
+ android:paddingRight="15dip"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/mtp_ptp_button"
+ android:text="@string/use_ptp_button_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clickable="true" />
+
+ <Button
+ android:id="@+id/installer_cd_button"
+ android:text="@string/installer_cd_button_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clickable="true" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8945da5..86e0cd0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -156,4 +156,13 @@
<!-- Compatibility mode help screen: body text. [CHAR LIMIT=150] -->
<string name="compat_mode_help_body">When an app was designed for a smaller screen, a zoom control will appear by the clock.</string>
+
+ <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
+ <string name="usb_preference_title">USB file transfer options</string>
+ <!-- Label for the MTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
+ <string name="use_mtp_button_title">Mount as a media player (MTP)</string>
+ <!-- Label for the PTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
+ <string name="use_ptp_button_title">Mount as a camera (PTP)</string>
+ <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] -->
+ <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java
new file mode 100644
index 0000000..3ed44e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.usb;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.util.Log;
+import android.widget.Button;
+
+import java.io.File;
+
+import com.android.systemui.R;
+
+public class UsbPreferenceActivity extends Activity implements View.OnClickListener {
+
+ private static final String TAG = "UsbPreferenceActivity";
+
+ private UsbManager mUsbManager;
+ private String mCurrentFunction;
+ private String[] mFunctions;
+ private String mInstallerImagePath;
+ private Button mMtpPtpButton;
+ private Button mInstallerCdButton;
+ private boolean mPtpActive;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+ dialogBuilder.setTitle(getString(R.string.usb_preference_title));
+
+ LayoutInflater inflater = (LayoutInflater)getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View buttonView = inflater.inflate(R.layout.usb_preference_buttons, null);
+ dialogBuilder.setView(buttonView);
+ mMtpPtpButton = (Button)buttonView.findViewById(R.id.mtp_ptp_button);
+ mInstallerCdButton = (Button)buttonView.findViewById(R.id.installer_cd_button);
+ mMtpPtpButton.setOnClickListener(this);
+ mInstallerCdButton.setOnClickListener(this);
+
+ mPtpActive = mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP);
+ if (mPtpActive) {
+ mMtpPtpButton.setText(R.string.use_mtp_button_title);
+ }
+
+ mInstallerImagePath = getString(com.android.internal.R.string.config_isoImagePath);
+ if (!(new File(mInstallerImagePath)).exists()) {
+ mInstallerCdButton.setVisibility(View.GONE);
+ }
+
+ dialogBuilder.show();
+ }
+
+ public void onClick(View v) {
+ if (v.equals(mMtpPtpButton)) {
+ if (mPtpActive) {
+ mUsbManager.setPrimaryFunction(UsbManager.USB_FUNCTION_MTP);
+ } else {
+ mUsbManager.setPrimaryFunction(UsbManager.USB_FUNCTION_PTP);
+ }
+ } else if (v.equals(mInstallerCdButton)) {
+ mUsbManager.setPrimaryFunction(UsbManager.USB_FUNCTION_MASS_STORAGE);
+ mUsbManager.setMassStorageBackingFile(mInstallerImagePath);
+ }
+
+ finish();
+ }
+}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c86f962..d3244ec 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -33,6 +33,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
@@ -152,7 +153,6 @@
* 600 series - Unsolicited broadcasts.
*/
public static final int VolumeStateChange = 605;
- public static final int ShareAvailabilityChange = 620;
public static final int VolumeDiskInserted = 630;
public static final int VolumeDiskRemoved = 631;
public static final int VolumeBadRemoval = 632;
@@ -167,6 +167,7 @@
private String mExternalStoragePath;
private PackageManagerService mPms;
private boolean mUmsEnabling;
+ private boolean mUmsAvailable = false;
// Used as a lock for methods that register/unregister listeners.
final private ArrayList<MountServiceBinderListener> mListeners =
new ArrayList<MountServiceBinderListener>();
@@ -525,6 +526,10 @@
}
}
}.start();
+ } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
+ boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
+ intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
+ notifyShareAvailabilityChange(available);
}
}
};
@@ -654,12 +659,6 @@
updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
}
- try {
- boolean avail = doGetShareMethodAvailable("ums");
- notifyShareAvailabilityChange("ums", avail);
- } catch (Exception ex) {
- Slog.w(TAG, "Failed to get share availability");
- }
/*
* Now that we've done our initialization, release
* the hounds!
@@ -694,13 +693,6 @@
notifyVolumeStateChange(
cooked[2], cooked[3], Integer.parseInt(cooked[7]),
Integer.parseInt(cooked[10]));
- } else if (code == VoldResponseCode.ShareAvailabilityChange) {
- // FMT: NNN Share method <method> now <available|unavailable>
- boolean avail = false;
- if (cooked[5].equals("available")) {
- avail = true;
- }
- notifyShareAvailabilityChange(cooked[3], avail);
} else if ((code == VoldResponseCode.VolumeDiskInserted) ||
(code == VoldResponseCode.VolumeDiskRemoved) ||
(code == VoldResponseCode.VolumeBadRemoval)) {
@@ -835,42 +827,6 @@
}
}
- private boolean doGetShareMethodAvailable(String method) {
- ArrayList<String> rsp;
- try {
- rsp = mConnector.doCommand("share status " + method);
- } catch (NativeDaemonConnectorException ex) {
- Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
- return false;
- }
-
- for (String line : rsp) {
- String[] tok = line.split(" ");
- if (tok.length < 3) {
- Slog.e(TAG, "Malformed response to share status " + method);
- return false;
- }
-
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
- return false;
- }
- if (code == VoldResponseCode.ShareStatusResult) {
- if (tok[2].equals("available"))
- return true;
- return false;
- } else {
- Slog.e(TAG, String.format("Unexpected response code %d", code));
- return false;
- }
- }
- Slog.e(TAG, "Got an empty response");
- return false;
- }
-
private int doMountVolume(String path) {
int rc = StorageResultCode.OperationSucceeded;
@@ -1018,13 +974,9 @@
return false;
}
- private void notifyShareAvailabilityChange(String method, final boolean avail) {
- if (!method.equals("ums")) {
- Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
- return;
- }
-
+ private void notifyShareAvailabilityChange(final boolean avail) {
synchronized (mListeners) {
+ mUmsAvailable = avail;
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
@@ -1189,8 +1141,13 @@
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ // don't bother monitoring USB if mass storage is not supported on our primary volume
+ if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
+ filter.addAction(UsbManager.ACTION_USB_STATE);
+ }
+ mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
mHandlerThread = new HandlerThread("MountService");
mHandlerThread.start();
@@ -1323,7 +1280,9 @@
if (getUmsEnabling()) {
return true;
}
- return doGetShareMethodAvailable("ums");
+ synchronized (mListeners) {
+ return mUmsAvailable;
+ }
}
public void setUsbMassStorageEnabled(boolean enable) {
@@ -1419,7 +1378,7 @@
return doFormatVolume(path);
}
- public int []getStorageUsers(String path) {
+ public int[] getStorageUsers(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2769004..a23bacf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -406,8 +406,8 @@
}
try {
- Slog.i(TAG, "USB Observer");
- // Listen for USB changes
+ Slog.i(TAG, "USB Service");
+ // Manage USB host and device support
usb = new UsbService(context);
ServiceManager.addService(Context.USB_SERVICE, usb);
} catch (Throwable e) {
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index ca8a184..b7f9d5c 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -19,7 +19,6 @@
import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -33,10 +32,16 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
import android.os.SystemProperties;
import android.os.UEventObserver;
import android.provider.Settings;
@@ -46,7 +51,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
-import java.io.FileReader;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -55,77 +60,110 @@
* UsbDeviceManager manages USB state in device mode.
*/
public class UsbDeviceManager {
+
private static final String TAG = UsbDeviceManager.class.getSimpleName();
private static final boolean LOG = false;
- private static final String USB_CONNECTED_MATCH =
- "DEVPATH=/devices/virtual/switch/usb_connected";
- private static final String USB_CONFIGURATION_MATCH =
- "DEVPATH=/devices/virtual/switch/usb_configuration";
- private static final String USB_FUNCTIONS_MATCH =
- "DEVPATH=/devices/virtual/usb_composite/";
- private static final String USB_CONNECTED_PATH =
- "/sys/class/switch/usb_connected/state";
- private static final String USB_CONFIGURATION_PATH =
- "/sys/class/switch/usb_configuration/state";
- private static final String USB_COMPOSITE_CLASS_PATH =
- "/sys/class/usb_composite";
+ private static final String USB_STATE_MATCH =
+ "DEVPATH=/devices/virtual/android_usb/android0";
+ private static final String ACCESSORY_START_MATCH =
+ "DEVPATH=/devices/virtual/misc/usb_accessory";
+ private static final String FUNCTIONS_PATH =
+ "/sys/class/android_usb/android0/functions";
+ private static final String STATE_PATH =
+ "/sys/class/android_usb/android0/state";
+ private static final String MASS_STORAGE_FILE_PATH =
+ "/sys/class/android_usb/f_mass_storage/lun/file";
private static final int MSG_UPDATE_STATE = 0;
- private static final int MSG_FUNCTION_ENABLED = 1;
- private static final int MSG_FUNCTION_DISABLED = 2;
+ private static final int MSG_ENABLE_ADB = 1;
+ private static final int MSG_SET_PRIMARY_FUNCTION = 2;
+ private static final int MSG_SET_DEFAULT_FUNCTION = 3;
+ private static final int MSG_SYSTEM_READY = 4;
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
private static final int UPDATE_DELAY = 1000;
- // current connected and configuration state
- private int mConnected;
- private int mConfiguration;
-
- // last broadcasted connected and configuration state
- private int mLastConnected = -1;
- private int mLastConfiguration = -1;
-
- // lists of enabled and disabled USB functions
- private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
-
+ private UsbHandler mHandler;
private boolean mSystemReady;
- private UsbAccessory mCurrentAccessory;
- // USB functions that are enabled by default, to restore after exiting accessory mode
- private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
-
private final Context mContext;
- ContentResolver mContentResolver;
- private final Object mLock = new Object();
+ private final ContentResolver mContentResolver;
private final UsbSettingsManager mSettingsManager;
private NotificationManager mNotificationManager;
private final boolean mHasUsbAccessory;
- // for adb connected notifications
- private boolean mAdbNotificationShown = false;
+ // for USB connected notification
+ private boolean mUsbNotificationShown;
+ private boolean mUseUsbNotification;
+ private Notification mUsbNotification;
+
+ // for adb connected notification
+ private boolean mAdbNotificationShown;
private Notification mAdbNotification;
private boolean mAdbEnabled;
+
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
super(null);
}
@Override
public void onChange(boolean selfChange) {
- mAdbEnabled = (Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ADB_ENABLED, 0) > 0);
- // setting this secure property will start or stop adbd
- SystemProperties.set("persist.service.adb.enable", mAdbEnabled ? "1" : "0");
- updateAdbNotification();
+ boolean enable = (Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ADB_ENABLED, 0) > 0);
+ mHandler.sendMessage(MSG_ENABLE_ADB, enable);
}
}
- private void updateAdbNotification() {
+ private void updateUsbNotification(boolean connected) {
+ if (mNotificationManager == null || !mUseUsbNotification) return;
+ if (connected) {
+ if (!mUsbNotificationShown) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.usb_preferences_notification_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.usb_preferece_notification_message);
+
+ if (mUsbNotification == null) {
+ mUsbNotification = new Notification();
+ mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
+ mUsbNotification.when = 0;
+ mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mUsbNotification.tickerText = title;
+ mUsbNotification.defaults = 0; // please be quiet
+ mUsbNotification.sound = null;
+ mUsbNotification.vibrate = null;
+ }
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbPreferenceActivity");
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+ intent, 0);
+
+ mUsbNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ mUsbNotificationShown = true;
+ mNotificationManager.notify(
+ com.android.internal.R.string.usb_preferences_notification_title,
+ mUsbNotification);
+ }
+
+ } else if (mUsbNotificationShown) {
+ mUsbNotificationShown = false;
+ mNotificationManager.cancel(
+ com.android.internal.R.string.usb_preferences_notification_title);
+ }
+ }
+
+ private void updateAdbNotification(boolean adbEnabled) {
if (mNotificationManager == null) return;
- boolean adbEnabled = mAdbEnabled && (mConnected == 1);
if (adbEnabled) {
if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
@@ -173,38 +211,6 @@
}
}
- private final void readCurrentAccessoryLocked() {
- if (mHasUsbAccessory) {
- String[] strings = nativeGetAccessoryStrings();
- if (strings != null) {
- mCurrentAccessory = new UsbAccessory(strings);
- Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
- if (mSystemReady) {
- mSettingsManager.accessoryAttached(mCurrentAccessory);
- }
- } else {
- Log.e(TAG, "nativeGetAccessoryStrings failed");
- }
- }
- }
-
- /*
- * Handles USB function enable/disable events
- */
- private final void functionEnabledLocked(String function, boolean enabled) {
- if (enabled) {
- if (!mEnabledFunctions.contains(function)) {
- mEnabledFunctions.add(function);
- }
-
- if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
- readCurrentAccessoryLocked();
- }
- } else {
- mEnabledFunctions.remove(function);
- }
- }
-
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
@@ -215,53 +221,13 @@
Slog.v(TAG, "USB UEVENT: " + event.toString());
}
- synchronized (mLock) {
- String name = event.get("SWITCH_NAME");
- String state = event.get("SWITCH_STATE");
- if (name != null && state != null) {
- try {
- int intState = Integer.parseInt(state);
- if ("usb_connected".equals(name)) {
- mConnected = intState;
- // trigger an Intent broadcast
- if (mSystemReady) {
- // debounce disconnects to avoid problems bringing up USB tethering
- update(mConnected == 0);
- }
- } else if ("usb_configuration".equals(name)) {
- mConfiguration = intState;
- // trigger an Intent broadcast
- if (mSystemReady) {
- update(mConnected == 0);
- }
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
- }
- } else {
- String function = event.get("FUNCTION");
- String enabledStr = event.get("ENABLED");
- if (function != null && enabledStr != null) {
- // Note: we do not broadcast a change when a function is enabled or disabled.
- // We just record the state change for the next broadcast.
- int what = ("1".equals(enabledStr) ?
- MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
- Message msg = Message.obtain(mHandler, what);
- msg.obj = function;
- mHandler.sendMessage(msg);
- }
- }
- }
- }
- };
-
- private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- // handle accessories attached at boot time
- synchronized (mLock) {
- if (mCurrentAccessory != null) {
- mSettingsManager.accessoryAttached(mCurrentAccessory);
- }
+ String state = event.get("USB_STATE");
+ String accessory = event.get("ACCESSORY");
+ if (state != null) {
+ mHandler.updateState(state);
+ } else if ("START".equals(accessory)) {
+ Slog.d(TAG, "got accessory start");
+ setPrimaryFunction(UsbManager.USB_FUNCTION_ACCESSORY);
}
}
};
@@ -273,229 +239,378 @@
PackageManager pm = mContext.getPackageManager();
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
- synchronized (mLock) {
- init(); // set initial status
-
- // make sure the ADB_ENABLED setting value matches the secure property value
- mAdbEnabled = "1".equals(SystemProperties.get("persist.service.adb.enable"));
- Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED,
- mAdbEnabled ? 1 : 0);
-
- // register observer to listen for settings changes
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
- false, new AdbSettingsObserver());
-
- // Watch for USB configuration changes
- if (mConfiguration >= 0) {
- mUEventObserver.startObserving(USB_CONNECTED_MATCH);
- mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
- mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
+ // create a thread for our Handler
+ HandlerThread thread = new HandlerThread("UsbDeviceManager",
+ Process.THREAD_PRIORITY_BACKGROUND) {
+ protected void onLooperPrepared() {
+ mHandler = new UsbHandler();
}
- }
- }
-
- private final void init() {
- char[] buffer = new char[1024];
- boolean inAccessoryMode = false;
-
- // Read initial USB state
- mConfiguration = -1;
- try {
- FileReader file = new FileReader(USB_CONNECTED_PATH);
- int len = file.read(buffer, 0, 1024);
- file.close();
- mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- file = new FileReader(USB_CONFIGURATION_PATH);
- len = file.read(buffer, 0, 1024);
- file.close();
- mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "This kernel does not have USB configuration switch support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- if (mConfiguration < 0) {
- // This may happen in the emulator or devices without USB device mode support
- return;
- }
-
- // Read initial list of enabled and disabled functions (device mode)
- try {
- File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
- for (int i = 0; i < files.length; i++) {
- File file = new File(files[i], "enable");
- FileReader reader = new FileReader(file);
- int len = reader.read(buffer, 0, 1024);
- reader.close();
- int value = Integer.valueOf((new String(buffer, 0, len)).trim());
- String functionName = files[i].getName();
- if (value == 1) {
- mEnabledFunctions.add(functionName);
- if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
- // The USB accessory driver is on by default, but it might have been
- // enabled before the USB service has initialized.
- inAccessoryMode = true;
- } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
- // adb is enabled/disabled automatically by the adbd daemon,
- // so don't treat it as a default function.
- mDefaultFunctions.add(functionName);
- }
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have USB composite class support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
-
- // handle the case where an accessory switched the driver to accessory mode
- // before the framework finished booting
- if (inAccessoryMode) {
- readCurrentAccessoryLocked();
-
- // FIXME - if we booted in accessory mode, then we have no way to figure out
- // which functions are enabled by default.
- // For now, assume that MTP or mass storage are the only possibilities
- if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
- mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
- } else if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
- mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
- }
- }
+ };
+ thread.start();
}
public void systemReady() {
- synchronized (mLock) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mSystemReady = true;
- update(false);
- if (mCurrentAccessory != null) {
- Log.d(TAG, "accessoryAttached at systemReady");
- // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
- // to handle this later.
- mContext.registerReceiver(mBootCompletedReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
- }
- mSystemReady = true;
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // We do not show the USB notification if the primary volume supports mass storage.
+ // The legacy mass storage UI will be used instead.
+ boolean massStorageSupported = false;
+ StorageManager storageManager = (StorageManager)
+ mContext.getSystemService(Context.STORAGE_SERVICE);
+ StorageVolume[] volumes = storageManager.getVolumeList();
+ if (volumes.length > 0) {
+ massStorageSupported = volumes[0].allowMassStorage();
}
+ mUseUsbNotification = !massStorageSupported;
+
+ // make sure the ADB_ENABLED setting value matches the current state
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+
+ mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
- /*
- * Sends a message to update the USB connected and configured state (device mode).
- * If delayed is true, then we add a small delay in sending the message to debounce
- * the USB connection when enabling USB tethering.
- */
- private final void update(boolean delayed) {
- mHandler.removeMessages(MSG_UPDATE_STATE);
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
- }
-
- /* returns the currently attached USB accessory (device mode) */
- public UsbAccessory getCurrentAccessory() {
- return mCurrentAccessory;
- }
-
- /* opens the currently attached USB accessory (device mode) */
- public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
- synchronized (mLock) {
- if (mCurrentAccessory == null) {
- throw new IllegalArgumentException("no accessory attached");
+ private static String addFunction(String functions, String function) {
+ if (!containsFunction(functions, function)) {
+ if (functions.length() > 0) {
+ functions += ",";
}
- if (!mCurrentAccessory.equals(accessory)) {
- Log.e(TAG, accessory.toString() + " does not match current accessory "
- + mCurrentAccessory);
- throw new IllegalArgumentException("accessory not attached");
- }
- mSettingsManager.checkPermission(mCurrentAccessory);
- return nativeOpenAccessory();
+ functions += function;
}
+ return functions;
}
- /*
- * This handler is for deferred handling of events related to device mode and accessories.
- */
- private final Handler mHandler = new Handler() {
+ private static String removeFunction(String functions, String function) {
+ String[] split = functions.split(",");
+ for (int i = 0; i < split.length; i++) {
+ if (function.equals(split[i])) {
+ split[i] = null;
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < split.length; i++) {
+ String s = split[i];
+ if (s != null) {
+ if (builder.length() > 0) {
+ builder.append(",");
+ }
+ builder.append(s);
+ }
+ }
+ return builder.toString();
+ }
- @Override
- public void handleMessage(Message msg) {
- synchronized (mLock) {
- switch (msg.what) {
- case MSG_UPDATE_STATE:
- if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
- updateAdbNotification();
- if (mConnected == 0) {
- if (UsbManager.isFunctionEnabled(
- UsbManager.USB_FUNCTION_ACCESSORY)) {
- // make sure accessory mode is off, and restore default functions
- Log.d(TAG, "exited USB accessory mode");
- if (!UsbManager.setFunctionEnabled
- (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
- Log.e(TAG, "could not disable accessory function");
- }
- int count = mDefaultFunctions.size();
- for (int i = 0; i < count; i++) {
- String function = mDefaultFunctions.get(i);
- if (!UsbManager.setFunctionEnabled(function, true)) {
- Log.e(TAG, "could not reenable function " + function);
- }
- }
+ private static boolean containsFunction(String functions, String function) {
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
+ }
- if (mCurrentAccessory != null) {
- mSettingsManager.accessoryDetached(mCurrentAccessory);
- mCurrentAccessory = null;
- }
- }
- }
+ private final class UsbHandler extends Handler {
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.Secure.getInt(cr,
- Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
- Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
- return;
- }
+ // current USB state
+ private boolean mConnected;
+ private boolean mConfigured;
+ private String mCurrentFunctions;
+ private String mDefaultFunctions;
+ private UsbAccessory mCurrentAccessory;
+ private boolean mDeferAccessoryAttached;
- mLastConnected = mConnected;
- mLastConfiguration = mConfiguration;
+ public UsbHandler() {
+ // Read initial USB state
+ try {
+ mCurrentFunctions = FileUtils.readTextFile(
+ new File(FUNCTIONS_PATH), 0, null).trim();
+ mDefaultFunctions = mCurrentFunctions;
+ String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+ updateState(state);
- // send a sticky broadcast containing current USB state
- Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
- intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- intent.putExtra(mEnabledFunctions.get(i), true);
- }
- mContext.sendStickyBroadcast(intent);
- }
- break;
- case MSG_FUNCTION_ENABLED:
- case MSG_FUNCTION_DISABLED:
- functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
- break;
+ mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+
+ // Upgrade step for previous versions that used persist.service.adb.enable
+ String value = SystemProperties.get("persist.service.adb.enable", "");
+ if (value.length() > 0) {
+ char enable = value.charAt(0);
+ if (enable == '1') {
+ setAdbEnabled(true);
+ } else if (enable == '0') {
+ setAdbEnabled(false);
+ }
+ SystemProperties.set("persist.service.adb.enable", "");
+ }
+
+ // register observer to listen for settings changes
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
+ false, new AdbSettingsObserver());
+
+ // Watch for USB configuration changes
+ mUEventObserver.startObserving(USB_STATE_MATCH);
+ mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error initializing UsbHandler", e);
+ }
+ }
+
+ public void sendMessage(int what, boolean arg) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.arg1 = (arg ? 1 : 0);
+ sendMessage(m);
+ }
+
+ public void sendMessage(int what, Object arg) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.obj = arg;
+ sendMessage(m);
+ }
+
+ public void updateState(String state) {
+ int connected, configured;
+
+ if ("DISCONNECTED".equals(state)) {
+ connected = 0;
+ configured = 0;
+ } else if ("CONNECTED".equals(state)) {
+ connected = 1;
+ configured = 0;
+ } else if ("CONFIGURED".equals(state)) {
+ connected = 1;
+ configured = 1;
+ } else {
+ Slog.e(TAG, "unknown state " + state);
+ return;
+ }
+ removeMessages(MSG_UPDATE_STATE);
+ Message msg = Message.obtain(this, MSG_UPDATE_STATE);
+ msg.arg1 = connected;
+ msg.arg2 = configured;
+ // debounce disconnects to avoid problems bringing up USB tethering
+ sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
+ }
+
+ private boolean setUsbConfig(String config) {
+ // set the new configuration
+ SystemProperties.set("sys.usb.config", config);
+ // wait for the transition to complete.
+ // give up after 1 second.
+ for (int i = 0; i < 20; i++) {
+ // State transition is done when sys.usb.conf.done is set to the new configuration
+ if (config.equals(SystemProperties.get("sys.usb.state"))) return true;
+ try {
+ // try again in 50ms
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ }
+ }
+ return false;
+ }
+
+ private void setCurrentFunctions(String functions) {
+ if (!mCurrentFunctions.equals(functions)) {
+ if (!setUsbConfig("none") || !setUsbConfig(functions)) {
+ Log.e(TAG, "Failed to switch USB configuration to " + functions);
+ // revert to previous configuration if we fail
+ setUsbConfig(mCurrentFunctions);
+ } else {
+ mCurrentFunctions = functions;
}
}
}
- };
+
+ private void setAdbEnabled(boolean enable) {
+ if (enable != mAdbEnabled) {
+ mAdbEnabled = enable;
+ String functions;
+ if (enable) {
+ functions = addFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+ mDefaultFunctions = addFunction(mDefaultFunctions,
+ UsbManager.USB_FUNCTION_ADB);
+ } else {
+ functions = removeFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+ mDefaultFunctions = removeFunction(mDefaultFunctions,
+ UsbManager.USB_FUNCTION_ADB);
+ }
+ SystemProperties.set("persist.sys.usb.config", mDefaultFunctions);
+ setCurrentFunctions(functions);
+ updateAdbNotification(mAdbEnabled && mConnected);
+ }
+ }
+
+ private void setEnabledFunctions(String functionList) {
+ if (mAdbEnabled) {
+ functionList = addFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+ } else {
+ functionList = removeFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+ }
+ setCurrentFunctions(functionList);
+ }
+
+ private void updateCurrentAccessory() {
+ if (!mHasUsbAccessory) return;
+
+ if (mConfigured) {
+ String[] strings = nativeGetAccessoryStrings();
+ if (strings != null) {
+ mCurrentAccessory = new UsbAccessory(strings);
+ Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+ // defer accessoryAttached if system is not ready
+ if (mSystemReady) {
+ mSettingsManager.accessoryAttached(mCurrentAccessory);
+ } else {
+ mDeferAccessoryAttached = true;
+ }
+ } else {
+ Log.e(TAG, "nativeGetAccessoryStrings failed");
+ }
+ } else if (!mConnected) {
+ // make sure accessory mode is off
+ // and restore default functions
+ Log.d(TAG, "exited USB accessory mode");
+ setEnabledFunctions(mDefaultFunctions);
+
+ if (mCurrentAccessory != null) {
+ if (mSystemReady) {
+ mSettingsManager.accessoryDetached(mCurrentAccessory);
+ }
+ mCurrentAccessory = null;
+ }
+ }
+ }
+
+ private void updateUsbState() {
+ // send a sticky broadcast containing current USB state
+ Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
+ intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
+
+ if (mCurrentFunctions != null) {
+ String[] functions = mCurrentFunctions.split(",");
+ for (int i = 0; i < functions.length; i++) {
+ intent.putExtra(functions[i], true);
+ }
+ }
+
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ String function;
+
+ switch (msg.what) {
+ case MSG_UPDATE_STATE:
+ mConnected = (msg.arg1 == 1);
+ mConfigured = (msg.arg2 == 1);
+ updateUsbNotification(mConnected);
+ updateAdbNotification(mAdbEnabled && mConnected);
+ if (containsFunction(mCurrentFunctions,
+ UsbManager.USB_FUNCTION_ACCESSORY)) {
+ updateCurrentAccessory();
+ }
+
+ if (!mConnected) {
+ // restore defaults when USB is disconnected
+ setCurrentFunctions(mDefaultFunctions);
+ }
+ if (mSystemReady) {
+ updateUsbState();
+ }
+ break;
+ case MSG_ENABLE_ADB:
+ setAdbEnabled(msg.arg1 == 1);
+ break;
+ case MSG_SET_PRIMARY_FUNCTION:
+ function = (String)msg.obj;
+ if (function == null) {
+ function = mDefaultFunctions;
+ }
+ setEnabledFunctions(function);
+ break;
+ case MSG_SET_DEFAULT_FUNCTION:
+ function = (String)msg.obj;
+ if (mAdbEnabled) {
+ function = addFunction(function, UsbManager.USB_FUNCTION_ADB);
+ }
+ SystemProperties.set("persist.sys.usb.config", function);
+ mDefaultFunctions = function;
+ break;
+ case MSG_SYSTEM_READY:
+ updateUsbNotification(mConnected);
+ updateAdbNotification(mAdbEnabled && mConnected);
+ updateUsbState();
+ if (mCurrentAccessory != null && mDeferAccessoryAttached) {
+ mSettingsManager.accessoryAttached(mCurrentAccessory);
+ }
+ break;
+ }
+ }
+
+ public UsbAccessory getCurrentAccessory() {
+ return mCurrentAccessory;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ pw.println(" USB Device State:");
+ pw.println(" Current Functions: " + mCurrentFunctions);
+ pw.println(" Default Functions: " + mDefaultFunctions);
+ pw.println(" mConnected: " + mConnected);
+ pw.println(" mConfigured: " + mConfigured);
+ pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ }
+ }
+
+ /* returns the currently attached USB accessory */
+ public UsbAccessory getCurrentAccessory() {
+ return mHandler.getCurrentAccessory();
+ }
+
+ /* opens the currently attached USB accessory */
+ public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+ UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
+ if (currentAccessory == null) {
+ throw new IllegalArgumentException("no accessory attached");
+ }
+ if (!currentAccessory.equals(accessory)) {
+ String error = accessory.toString()
+ + " does not match current accessory "
+ + currentAccessory;
+ throw new IllegalArgumentException(error);
+ }
+ mSettingsManager.checkPermission(accessory);
+ return nativeOpenAccessory();
+ }
+
+ public void setPrimaryFunction(String function) {
+ mHandler.sendMessage(MSG_SET_PRIMARY_FUNCTION, function);
+ }
+
+ public void setDefaultFunction(String function) {
+ if (function == null) {
+ throw new NullPointerException();
+ }
+ mHandler.sendMessage(MSG_SET_DEFAULT_FUNCTION, function);
+ }
+
+ public void setMassStorageBackingFile(String path) {
+ if (path == null) path = "";
+ try {
+ FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path);
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH);
+ }
+ }
public void dump(FileDescriptor fd, PrintWriter pw) {
- synchronized (mLock) {
- pw.println(" USB Device State:");
- pw.print(" Enabled Functions: ");
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- pw.print(mEnabledFunctions.get(i) + " ");
- }
- pw.println("");
- pw.print(" Default Functions: ");
- for (int i = 0; i < mDefaultFunctions.size(); i++) {
- pw.print(mDefaultFunctions.get(i) + " ");
- }
- pw.println("");
- pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
- pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ if (mHandler != null) {
+ mHandler.dump(fd, pw);
}
}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 21e5997c..193638f 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -50,7 +50,7 @@
if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
mHostManager = new UsbHostManager(context, mSettingsManager);
}
- if (new File("/sys/class/usb_composite").exists()) {
+ if (new File("/sys/class/android_usb").exists()) {
mDeviceManager = new UsbDeviceManager(context, mSettingsManager);
}
}
@@ -92,7 +92,7 @@
/* opens the currently attached USB accessory (device mode) */
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
if (mDeviceManager != null) {
- return openAccessory(accessory);
+ return mDeviceManager.openAccessory(accessory);
} else {
return null;
}
@@ -146,6 +146,33 @@
mSettingsManager.clearDefaults(packageName);
}
+ public void setPrimaryFunction(String function) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ if (mDeviceManager != null) {
+ mDeviceManager.setPrimaryFunction(function);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
+ public void setDefaultFunction(String function) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ if (mDeviceManager != null) {
+ mDeviceManager.setDefaultFunction(function);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
+ public void setMassStorageBackingFile(String path) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ if (mDeviceManager != null) {
+ mDeviceManager.setMassStorageBackingFile(path);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index d5dcd4e..c650021 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -31,6 +31,15 @@
android:hardwareAccelerated="true">
<activity
+ android:name="GetBitmapActivity"
+ android:label="_GetBitmap">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="SmallCircleActivity"
android:label="_SmallCircle">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
new file mode 100644
index 0000000..2e23aaa
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Gravity;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GetBitmapActivity extends Activity implements TextureView.SurfaceTextureListener {
+ private Camera mCamera;
+ private TextureView mTextureView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout content = new FrameLayout(this);
+
+ mTextureView = new TextureView(this);
+ mTextureView.setSurfaceTextureListener(this);
+
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/textureview.png");
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Bitmap b = mTextureView.getBitmap();
+ try {
+ FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/textureview.png");
+ try {
+ b.compress(Bitmap.CompressFormat.PNG, 100, out);
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Ignore
+ }
+ }
+ });
+
+ content.addView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+ content.addView(button, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+ setContentView(content);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ mCamera = Camera.open();
+
+ try {
+ mCamera.setPreviewTexture(surface);
+ } catch (IOException t) {
+ android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
+ }
+
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ // Ignored, the Camera does all the work for us
+ }
+
+ @Override
+ public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ mCamera.stopPreview();
+ mCamera.release();
+ }
+}