diff options
Diffstat (limited to 'core/java/android')
23 files changed, 1863 insertions, 65 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4003dc0..e4bb5be 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4277,7 +4277,7 @@ public class Activity extends ContextThemeWrapper /** * Print the Activity's state into the given stream. This gets invoked if - * you run "adb shell dumpsys activity <activity_component_name>". + * you run "adb shell dumpsys activity <activity_component_name>". * * @param prefix Desired prefix to prepend at each line of output. * @param fd The raw file descriptor that the dump is being sent to. @@ -4318,7 +4318,6 @@ public class Activity extends ContextThemeWrapper * {@link #setImmersive}. * * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE - * @hide */ public boolean isImmersive() { try { @@ -4341,7 +4340,6 @@ public class Activity extends ContextThemeWrapper * * @see #isImmersive * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE - * @hide */ public void setImmersive(boolean i) { try { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 2bf1fb7..bb483d1 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -42,7 +42,9 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.hardware.ISerialManager; import android.hardware.SensorManager; +import android.hardware.SerialManager; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbManager; import android.location.CountryDetector; @@ -427,6 +429,12 @@ class ContextImpl extends Context { return new UsbManager(ctx, IUsbManager.Stub.asInterface(b)); }}); + registerService(SERIAL_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(SERIAL_SERVICE); + return new SerialManager(ctx, ISerialManager.Stub.asInterface(b)); + }}); + registerService(VIBRATOR_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new Vibrator(); diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index e8d10cb..35bd8c0 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -654,8 +654,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac /** * Print the Service's state into the given stream. This gets invoked if - * you run "adb shell dumpsys activity service <yourservicename>". - * This is distinct from "dumpsys <servicename>", which only works for + * you run "adb shell dumpsys activity service <yourservicename>". + * This is distinct from "dumpsys <servicename>", which only works for * named system services and which invokes the {@link IBinder#dump} method * on the {@link IBinder} interface registered with ServiceManager. * diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 71f6445..0c22740 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -168,7 +168,7 @@ public class UiModeManager { * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or - * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TV}. + * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_APPLIANCE}. */ public int getCurrentModeType() { if (mService != null) { diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index b1c1f30..a2b495f 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -437,7 +437,12 @@ public class WallpaperManager { */ public WallpaperInfo getWallpaperInfo() { try { - return sGlobals.mService.getWallpaperInfo(); + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return null; + } else { + return sGlobals.mService.getWallpaperInfo(); + } } catch (RemoteException e) { return null; } @@ -455,6 +460,10 @@ public class WallpaperManager { * wallpaper. */ public void setResource(int resid) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } try { Resources resources = mContext.getResources(); /* Set the wallpaper to the default values */ @@ -487,6 +496,10 @@ public class WallpaperManager { * wallpaper. */ public void setBitmap(Bitmap bitmap) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); if (fd == null) { @@ -519,6 +532,10 @@ public class WallpaperManager { * wallpaper. */ public void setStream(InputStream data) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); if (fd == null) { @@ -562,6 +579,10 @@ public class WallpaperManager { * mandatory. */ public int getDesiredMinimumWidth() { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return 0; + } try { return sGlobals.mService.getWidthHint(); } catch (RemoteException e) { @@ -585,6 +606,10 @@ public class WallpaperManager { * mandatory. */ public int getDesiredMinimumHeight() { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return 0; + } try { return sGlobals.mService.getHeightHint(); } catch (RemoteException e) { @@ -603,7 +628,11 @@ public class WallpaperManager { */ public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { try { - sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight); + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + } else { + sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight); + } } catch (RemoteException e) { // Ignore } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bfbd0ac..6f1add0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1627,6 +1627,17 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link + * android.os.IUpdateLock} for managing runtime sequences that + * must not be interrupted by headless OTA application or similar. + * + * @hide + * @see #getSystemService + * @see android.os.UpdateLock + */ + public static final String UPDATE_LOCK_SERVICE = "updatelock"; + + /** + * Use with {@link #getSystemService} to retrieve a {@link * android.net.NetworkManagementService} for handling management of * system network services * @@ -1789,6 +1800,17 @@ public abstract class Context { public static final String USB_SERVICE = "usb"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.hardware.SerialManager} for access to serial ports. + * + * @see #getSystemService + * @see android.harware.SerialManager + * + * @hide + */ + public static final String SERIAL_SERVICE = "serial"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 0e6694d..5506c7f 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -155,7 +155,6 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_HARDWARE_ACCELERATED = 0x0200; /** - * @hide * Bit in {@link #flags} corresponding to an immersive activity * that wishes not to be interrupted by notifications. * Applications that hide the system notification bar with diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 5c3a17a..6015668 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -228,6 +228,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration public static final int UI_MODE_TYPE_DESK = 0x02; public static final int UI_MODE_TYPE_CAR = 0x03; public static final int UI_MODE_TYPE_TELEVISION = 0x04; + public static final int UI_MODE_TYPE_APPLIANCE = 0x05; public static final int UI_MODE_NIGHT_MASK = 0x30; public static final int UI_MODE_NIGHT_UNDEFINED = 0x00; @@ -239,7 +240,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED}, * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK}, - * or {@link #UI_MODE_TYPE_CAR}. + * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, or + * {@link #UI_MODE_TYPE_APPLIANCE}. * * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED}, @@ -391,6 +393,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration case UI_MODE_TYPE_DESK: sb.append(" desk"); break; case UI_MODE_TYPE_CAR: sb.append(" car"); break; case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break; + case UI_MODE_TYPE_APPLIANCE: sb.append(" appliance"); break; default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; } switch ((uiMode&UI_MODE_NIGHT_MASK)) { diff --git a/core/java/android/hardware/ISerialManager.aidl b/core/java/android/hardware/ISerialManager.aidl new file mode 100644 index 0000000..74d30f7 --- /dev/null +++ b/core/java/android/hardware/ISerialManager.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.os.ParcelFileDescriptor; + +/** @hide */ +interface ISerialManager +{ + /* Returns a list of all available serial ports */ + String[] getSerialPorts(); + + /* Returns a file descriptor for the serial port. */ + ParcelFileDescriptor openSerialPort(String name); +} diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java new file mode 100644 index 0000000..c5e1c2b --- /dev/null +++ b/core/java/android/hardware/SerialManager.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.hardware; + +import android.app.PendingIntent; +import android.content.Context; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.util.Log; + +import java.io.IOException; +import java.util.HashMap; + +/** + * @hide + */ +public class SerialManager { + private static final String TAG = "SerialManager"; + + private final Context mContext; + private final ISerialManager mService; + + /** + * {@hide} + */ + public SerialManager(Context context, ISerialManager service) { + mContext = context; + mService = service; + } + + /** + * Returns a string array containing the names of available serial ports + * + * @return names of available serial ports + */ + public String[] getSerialPorts() { + try { + return mService.getSerialPorts(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getSerialPorts", e); + return null; + } + } + + /** + * Opens and returns the {@link android.hardware.SerialPort} with the given name. + * The speed of the serial port must be one of: + * 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, + * 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, + * 1500000, 2000000, 2500000, 3000000, 3500000 or 4000000 + * + * @param name of the serial port + * @param speed at which to open the serial port + * @return the serial port + */ + public SerialPort openSerialPort(String name, int speed) throws IOException { + try { + ParcelFileDescriptor pfd = mService.openSerialPort(name); + if (pfd != null) { + SerialPort port = new SerialPort(name); + port.open(pfd, speed); + return port; + } else { + throw new IOException("Could not open serial port " + name); + } + } catch (RemoteException e) { + Log.e(TAG, "exception in UsbManager.openDevice", e); + } + return null; + } +} diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java new file mode 100644 index 0000000..5ef122b --- /dev/null +++ b/core/java/android/hardware/SerialPort.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.nio.ByteBuffer; + +/** + * @hide + */ +public class SerialPort { + + private static final String TAG = "SerialPort"; + + // used by the JNI code + private int mNativeContext; + private final String mName; + private ParcelFileDescriptor mFileDescriptor; + + /** + * SerialPort should only be instantiated by SerialManager + * @hide + */ + public SerialPort(String name) { + mName = name; + } + + /** + * SerialPort should only be instantiated by SerialManager + * Speed must be one of 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, + * 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, + * 1500000, 2000000, 2500000, 3000000, 3500000, 4000000 + * + * @hide + */ + public void open(ParcelFileDescriptor pfd, int speed) throws IOException { + native_open(pfd.getFileDescriptor(), speed); + mFileDescriptor = pfd; + } + + /** + * Closes the serial port + */ + public void close() throws IOException { + if (mFileDescriptor != null) { + mFileDescriptor.close(); + mFileDescriptor = null; + } + native_close(); + } + + /** + * Returns the name of the serial port + * + * @return the serial port's name + */ + public String getName() { + return mName; + } + + /** + * Reads data into the provided buffer + * + * @param buffer to read into + * @return number of bytes read + */ + public int read(ByteBuffer buffer) throws IOException { + if (buffer.isDirect()) { + return native_read_direct(buffer, buffer.remaining()); + } else if (buffer.hasArray()) { + return native_read_array(buffer.array(), buffer.remaining()); + } else { + throw new IllegalArgumentException("buffer is not direct and has no array"); + } + } + + /** + * Writes data from provided buffer + * + * @param buffer to write + * @param length number of bytes to write + */ + public void write(ByteBuffer buffer, int length) throws IOException { + if (buffer.isDirect()) { + native_write_direct(buffer, length); + } else if (buffer.hasArray()) { + native_write_array(buffer.array(), length); + } else { + throw new IllegalArgumentException("buffer is not direct and has no array"); + } + } + + /** + * Sends a stream of zero valued bits for 0.25 to 0.5 seconds + */ + public void sendBreak() { + native_send_break(); + } + + private native void native_open(FileDescriptor pfd, int speed) throws IOException; + private native void native_close(); + private native int native_read_array(byte[] buffer, int length) throws IOException; + private native int native_read_direct(ByteBuffer buffer, int length) throws IOException; + private native void native_write_array(byte[] buffer, int length) throws IOException; + private native void native_write_direct(ByteBuffer buffer, int length) throws IOException; + private native void native_send_break(); +} diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 21ecc22..b1dd62c 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -49,6 +49,7 @@ public class EthernetDataTracker implements NetworkStateTracker { private LinkCapabilities mLinkCapabilities; private NetworkInfo mNetworkInfo; private InterfaceObserver mInterfaceObserver; + private String mHwAddr; /* For sending events to connectivity service handler */ private Handler mCsHandler; @@ -74,15 +75,13 @@ public class EthernetDataTracker implements NetworkStateTracker { if (mIface.equals(iface) && mLinkUp != up) { Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down")); mLinkUp = up; + mTracker.mNetworkInfo.setIsAvailable(up); // use DHCP if (up) { mTracker.reconnect(); } else { - NetworkUtils.stopDhcp(mIface); - mTracker.mNetworkInfo.setIsAvailable(false); - mTracker.mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, - null, null); + mTracker.disconnect(); } } } @@ -104,10 +103,6 @@ public class EthernetDataTracker implements NetworkStateTracker { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, ""); mLinkProperties = new LinkProperties(); mLinkCapabilities = new LinkCapabilities(); - mLinkUp = false; - - mNetworkInfo.setIsAvailable(false); - setTeardownRequested(false); } private void interfaceAdded(String iface) { @@ -116,7 +111,7 @@ public class EthernetDataTracker implements NetworkStateTracker { Log.d(TAG, "Adding " + iface); - synchronized(mIface) { + synchronized(this) { if(!mIface.isEmpty()) return; mIface = iface; @@ -125,21 +120,15 @@ public class EthernetDataTracker implements NetworkStateTracker { mNetworkInfo.setIsAvailable(true); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); - - runDhcp(); } - private void interfaceRemoved(String iface) { - if (!iface.equals(mIface)) - return; - - Log.d(TAG, "Removing " + iface); + public void disconnect() { NetworkUtils.stopDhcp(mIface); mLinkProperties.clear(); mNetworkInfo.setIsAvailable(false); - mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); + mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); @@ -147,6 +136,21 @@ public class EthernetDataTracker implements NetworkStateTracker { msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + try { + service.clearInterfaceAddresses(mIface); + } catch (Exception e) { + Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); + } + } + + private void interfaceRemoved(String iface) { + if (!iface.equals(mIface)) + return; + + Log.d(TAG, "Removing " + iface); + disconnect(); mIface = ""; } @@ -161,7 +165,7 @@ public class EthernetDataTracker implements NetworkStateTracker { mLinkProperties = dhcpInfoInternal.makeLinkProperties(); mLinkProperties.setInterfaceName(mIface); - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); } @@ -208,8 +212,15 @@ public class EthernetDataTracker implements NetworkStateTracker { for (String iface : ifaces) { if (iface.matches(sIfaceMatch)) { mIface = iface; + service.setInterfaceUp(iface); InterfaceConfiguration config = service.getInterfaceConfig(iface); mLinkUp = config.isActive(); + if (config != null && mHwAddr == null) { + mHwAddr = config.hwAddr; + if (mHwAddr != null) { + mNetworkInfo.setExtraInfo(mHwAddr); + } + } reconnect(); break; } @@ -239,9 +250,11 @@ public class EthernetDataTracker implements NetworkStateTracker { * Re-enable connectivity to a network after a {@link #teardown()}. */ public boolean reconnect() { - mTeardownRequested.set(false); - runDhcp(); - return true; + if (mLinkUp) { + mTeardownRequested.set(false); + runDhcp(); + } + return mLinkUp; } /** diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 537750a..1e645a1 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -346,6 +346,18 @@ public class NetworkInfo implements Parcelable { } /** + * Set the extraInfo field. + * @param extraInfo an optional {@code String} providing addditional network state + * information passed up from the lower networking layers. + * @hide + */ + public void setExtraInfo(String extraInfo) { + synchronized (this) { + this.mExtraInfo = extraInfo; + } + } + + /** * Report the reason an attempt to establish connectivity failed, * if one is available. * @return the reason for failure, or null if not available diff --git a/core/java/android/os/CommonClock.java b/core/java/android/os/CommonClock.java new file mode 100644 index 0000000..e728144 --- /dev/null +++ b/core/java/android/os/CommonClock.java @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2012 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.os; + +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.util.NoSuchElementException; +import static libcore.io.OsConstants.*; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; + +class CommonTimeUtils { + /** + * Successful operation. + */ + public static final int SUCCESS = 0; + /** + * Unspecified error. + */ + public static final int ERROR = -1; + /** + * Operation failed due to bad parameter value. + */ + public static final int ERROR_BAD_VALUE = -4; + /** + * Operation failed due to dead remote object. + */ + public static final int ERROR_DEAD_OBJECT = -7; + + public CommonTimeUtils(IBinder remote, String interfaceDesc) { + mRemote = remote; + mInterfaceDesc = interfaceDesc; + } + + public int transactGetInt(int method_code, int error_ret_val) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + int ret_val; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + ret_val = (0 == res) ? reply.readInt() : error_ret_val; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetInt(int method_code, int val) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeInt(val); + mRemote.transact(method_code, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + public long transactGetLong(int method_code, long error_ret_val) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + long ret_val; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + ret_val = (0 == res) ? reply.readLong() : error_ret_val; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetLong(int method_code, long val) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeLong(val); + mRemote.transact(method_code, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + public String transactGetString(int method_code, String error_ret_val) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + String ret_val; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + ret_val = (0 == res) ? reply.readString() : error_ret_val; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetString(int method_code, String val) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeString(val); + mRemote.transact(method_code, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + public InetSocketAddress transactGetSockaddr(int method_code) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + InetSocketAddress ret_val = null; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + if (0 == res) { + int type; + int port = 0; + String addrStr = null; + + type = reply.readInt(); + + if (AF_INET == type) { + int addr = reply.readInt(); + port = reply.readInt(); + addrStr = String.format("%d.%d.%d.%d", (addr >> 24) & 0xFF, + (addr >> 16) & 0xFF, + (addr >> 8) & 0xFF, + addr & 0xFF); + } else if (AF_INET6 == type) { + int addr1 = reply.readInt(); + int addr2 = reply.readInt(); + int addr3 = reply.readInt(); + int addr4 = reply.readInt(); + + port = reply.readInt(); + + int flowinfo = reply.readInt(); + int scope_id = reply.readInt(); + + addrStr = String.format("[%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X]", + (addr1 >> 16) & 0xFFFF, addr1 & 0xFFFF, + (addr2 >> 16) & 0xFFFF, addr2 & 0xFFFF, + (addr3 >> 16) & 0xFFFF, addr3 & 0xFFFF, + (addr4 >> 16) & 0xFFFF, addr4 & 0xFFFF); + } + + if (null != addrStr) { + ret_val = new InetSocketAddress(addrStr, port); + } + } + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetSockaddr(int method_code, InetSocketAddress addr) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + int ret_val = ERROR; + + try { + data.writeInterfaceToken(mInterfaceDesc); + + if (null == addr) { + data.writeInt(0); + } else { + data.writeInt(1); + final InetAddress a = addr.getAddress(); + final byte[] b = a.getAddress(); + final int p = addr.getPort(); + + if (a instanceof Inet4Address) { + int v4addr = (((int)b[0] & 0xFF) << 24) | + (((int)b[1] & 0xFF) << 16) | + (((int)b[2] & 0xFF) << 8) | + ((int)b[3] & 0xFF); + + data.writeInt(AF_INET); + data.writeInt(v4addr); + data.writeInt(p); + } else + if (a instanceof Inet6Address) { + int i; + Inet6Address v6 = (Inet6Address)a; + data.writeInt(AF_INET6); + for (i = 0; i < 4; ++i) { + int aword = (((int)b[(i*4) + 0] & 0xFF) << 24) | + (((int)b[(i*4) + 1] & 0xFF) << 16) | + (((int)b[(i*4) + 2] & 0xFF) << 8) | + ((int)b[(i*4) + 3] & 0xFF); + data.writeInt(aword); + } + data.writeInt(p); + data.writeInt(0); // flow info + data.writeInt(v6.getScopeId()); + } else { + return ERROR_BAD_VALUE; + } + } + + mRemote.transact(method_code, data, reply, 0); + ret_val = reply.readInt(); + } + catch (RemoteException e) { + ret_val = ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + private IBinder mRemote; + private String mInterfaceDesc; +}; + +/** + * Used for accessing the android common time service's common clock and receiving notifications + * about common time synchronization status changes. + * @hide + */ +public class CommonClock { + /** + * Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the + * common time service is not able to determine the current common time due to a lack of + * synchronization. + */ + public static final long TIME_NOT_SYNCED = -1; + + /** + * Sentinel value returned by {@link #getTimelineId()} when the common time service is not + * currently synced to any timeline. + */ + public static final long INVALID_TIMELINE_ID = 0; + + /** + * Sentinel value returned by {@link #getEstimatedError()} when the common time service is not + * currently synced to any timeline. + */ + public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF; + + /** + * Value used by {@link #getState()} to indicate that there was an internal error while + * attempting to determine the state of the common time service. + */ + public static final int STATE_INVALID = -1; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its initial + * state and attempting to find the current timeline master, if any. The service will + * transition to either {@link #STATE_CLIENT} if it finds an active master, or to + * {@link #STATE_MASTER} if no active master is found and this client becomes the master of a + * new timeline. + */ + public static final int STATE_INITIAL = 0; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its client + * state and is synchronizing its time to a different timeline master on the network. + */ + public static final int STATE_CLIENT = 1; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its master + * state and is serving as the timeline master for other common time service clients on the + * network. + */ + public static final int STATE_MASTER = 2; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its Ronin + * state. Common time service instances in the client state enter the Ronin state after their + * timeline master becomes unreachable on the network. Common time services who enter the Ronin + * state will begin a new master election for the timeline they were recently clients of. As + * clients detect they are not the winner and drop out of the election, they will transition to + * the {@link #STATE_WAIT_FOR_ELECTION} state. When there is only one client remaining in the + * election, it will assume ownership of the timeline and transition to the + * {@link #STATE_MASTER} state. During the election, all clients will allow their timeline to + * drift without applying correction. + */ + public static final int STATE_RONIN = 3; + + /** + * Value used by {@link #getState()} to indicate that the common time service is waiting for a + * master election to conclude and for the new master to announce itself before transitioning to + * the {@link #STATE_CLIENT} state. If no new master announces itself within the timeout + * threshold, the time service will transition back to the {@link #STATE_RONIN} state in order + * to restart the election. + */ + public static final int STATE_WAIT_FOR_ELECTION = 4; + + /** + * Name of the underlying native binder service + */ + public static final String SERVICE_NAME = "common_time.clock"; + + /** + * Class constructor. + * @throws android.os.RemoteException + */ + public CommonClock() + throws RemoteException { + mRemote = ServiceManager.getService(SERVICE_NAME); + if (null == mRemote) + throw new RemoteException(); + + mInterfaceDesc = mRemote.getInterfaceDescriptor(); + mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc); + mRemote.linkToDeath(mDeathHandler, 0); + registerTimelineChangeListener(); + } + + /** + * Handy class factory method. + */ + static public CommonClock create() { + CommonClock retVal; + + try { + retVal = new CommonClock(); + } + catch (RemoteException e) { + retVal = null; + } + + return retVal; + } + + /** + * Release all native resources held by this {@link android.os.CommonClock} instance. Once + * resources have been released, the {@link android.os.CommonClock} instance is disconnected from + * the native service and will throw a {@link android.os.RemoteException} if any of its + * methods are called. Clients should always call release on their client instances before + * releasing their last Java reference to the instance. Failure to do this will cause + * non-deterministic native resource reclamation and may cause the common time service to remain + * active on the network for longer than it should. + */ + public void release() { + unregisterTimelineChangeListener(); + if (null != mRemote) { + try { + mRemote.unlinkToDeath(mDeathHandler, 0); + } + catch (NoSuchElementException e) { } + mRemote = null; + } + mUtils = null; + } + + /** + * Gets the common clock's current time. + * + * @return a signed 64-bit value representing the current common time in microseconds, or the + * special value {@link #TIME_NOT_SYNCED} if the common time service is currently not + * synchronized. + * @throws android.os.RemoteException + */ + public long getTime() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED); + } + + /** + * Gets the current estimation of common clock's synchronization accuracy from the common time + * service. + * + * @return a signed 32-bit value representing the common time service's estimation of + * synchronization accuracy in microseconds, or the special value + * {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized. + * Negative values indicate that the local server estimates that the nominal common time is + * behind the local server's time (in other words, the local clock is running fast) Positive + * values indicate that the local server estimates that the nominal common time is ahead of the + * local server's time (in other words, the local clock is running slow) + * @throws android.os.RemoteException + */ + public int getEstimatedError() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN); + } + + /** + * Gets the ID of the timeline the common time service is currently synchronizing its clock to. + * + * @return a long representing the unique ID of the timeline the common time service is + * currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is + * currently not synchronized. + * @throws android.os.RemoteException + */ + public long getTimelineId() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID); + } + + /** + * Gets the current state of this clock's common time service in the the master election + * algorithm. + * + * @return a integer indicating the current state of the this clock's common time service in the + * master election algorithm or {@link #STATE_INVALID} if there is an internal error. + * @throws android.os.RemoteException + */ + public int getState() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID); + } + + /** + * Gets the IP address and UDP port of the current timeline master. + * + * @return an InetSocketAddress containing the IP address and UDP port of the current timeline + * master, or null if there is no current master. + * @throws android.os.RemoteException + */ + public InetSocketAddress getMasterAddr() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS); + } + + /** + * The OnTimelineChangedListener interface defines a method called by the + * {@link android.os.CommonClock} instance to indicate that the time synchronization service has + * either synchronized with a new timeline, or is no longer a member of any timeline. The + * client application can implement this interface and register the listener with the + * {@link #setTimelineChangedListener(OnTimelineChangedListener)} method. + */ + public interface OnTimelineChangedListener { + /** + * Method called when the time service's timeline has changed. + * + * @param newTimelineId a long which uniquely identifies the timeline the time + * synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the + * service is not synchronized to any timeline. + */ + void onTimelineChanged(long newTimelineId); + } + + /** + * Registers an OnTimelineChangedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + */ + public void setTimelineChangedListener(OnTimelineChangedListener listener) { + synchronized (mListenerLock) { + mTimelineChangedListener = listener; + } + } + + /** + * The OnServerDiedListener interface defines a method called by the + * {@link android.os.CommonClock} instance to indicate that the connection to the native media + * server has been broken and that the {@link android.os.CommonClock} instance will need to be + * released and re-created. The client application can implement this interface and register + * the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method. + */ + public interface OnServerDiedListener { + /** + * Method called when the native media server has died. <p>If the native common time + * service encounters a fatal error and needs to restart, the binder connection from the + * {@link android.os.CommonClock} instance to the common time service will be broken. To + * restore functionality, clients should {@link #release()} their old visualizer and create + * a new instance. + */ + void onServerDied(); + } + + /** + * Registers an OnServerDiedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + */ + public void setServerDiedListener(OnServerDiedListener listener) { + synchronized (mListenerLock) { + mServerDiedListener = listener; + } + } + + protected void finalize() throws Throwable { release(); } + + private void throwOnDeadServer() throws RemoteException { + if ((null == mRemote) || (null == mUtils)) + throw new RemoteException(); + } + + private final Object mListenerLock = new Object(); + private OnTimelineChangedListener mTimelineChangedListener = null; + private OnServerDiedListener mServerDiedListener = null; + + private IBinder mRemote = null; + private String mInterfaceDesc = ""; + private CommonTimeUtils mUtils; + + private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() { + public void binderDied() { + synchronized (mListenerLock) { + if (null != mServerDiedListener) + mServerDiedListener.onServerDied(); + } + } + }; + + private class TimelineChangedListener extends Binder { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + switch (code) { + case METHOD_CBK_ON_TIMELINE_CHANGED: + data.enforceInterface(DESCRIPTOR); + long timelineId = data.readLong(); + synchronized (mListenerLock) { + if (null != mTimelineChangedListener) + mTimelineChangedListener.onTimelineChanged(timelineId); + } + return true; + } + + return super.onTransact(code, data, reply, flags); + } + + private static final String DESCRIPTOR = "android.os.ICommonClockListener"; + }; + + private TimelineChangedListener mCallbackTgt = null; + + private void registerTimelineChangeListener() throws RemoteException { + if (null != mCallbackTgt) + return; + + boolean success = false; + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + mCallbackTgt = new TimelineChangedListener(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeStrongBinder(mCallbackTgt); + mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0); + success = (0 == reply.readInt()); + } + catch (RemoteException e) { + success = false; + } + finally { + reply.recycle(); + data.recycle(); + } + + // Did we catch a remote exception or fail to register our callback target? If so, our + // object must already be dead (or be as good as dead). Clear out all of our state so that + // our other methods will properly indicate a dead object. + if (!success) { + mCallbackTgt = null; + mRemote = null; + mUtils = null; + } + } + + private void unregisterTimelineChangeListener() { + if (null == mCallbackTgt) + return; + + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeStrongBinder(mCallbackTgt); + mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0); + } + catch (RemoteException e) { } + finally { + reply.recycle(); + data.recycle(); + mCallbackTgt = null; + } + } + + private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION; + private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1; + private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1; + private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1; + private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1; + private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1; + private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1; + private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1; + private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1; + private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1; + private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1; + private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1; + private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1; + + private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION; +} diff --git a/core/java/android/os/CommonTimeConfig.java b/core/java/android/os/CommonTimeConfig.java new file mode 100644 index 0000000..6b0f4fc --- /dev/null +++ b/core/java/android/os/CommonTimeConfig.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2012 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.os; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.NoSuchElementException; + +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; + +/** + * Used for configuring and controlling the status of the android common time service. + * @hide + */ +public class CommonTimeConfig { + /** + * Successful operation. + */ + public static final int SUCCESS = 0; + /** + * Unspecified error. + */ + public static final int ERROR = -1; + /** + * Operation failed due to bad parameter value. + */ + public static final int ERROR_BAD_VALUE = -4; + /** + * Operation failed due to dead remote object. + */ + public static final int ERROR_DEAD_OBJECT = -7; + + /** + * Sentinel value returned by {@link #getMasterElectionGroupId()} when an error occurs trying to + * fetch the master election group. + */ + public static final long INVALID_GROUP_ID = -1; + + /** + * Name of the underlying native binder service + */ + public static final String SERVICE_NAME = "common_time.config"; + + /** + * Class constructor. + * @throws android.os.RemoteException + */ + public CommonTimeConfig() + throws RemoteException { + mRemote = ServiceManager.getService(SERVICE_NAME); + if (null == mRemote) + throw new RemoteException(); + + mInterfaceDesc = mRemote.getInterfaceDescriptor(); + mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc); + mRemote.linkToDeath(mDeathHandler, 0); + } + + /** + * Handy class factory method. + */ + static public CommonTimeConfig create() { + CommonTimeConfig retVal; + + try { + retVal = new CommonTimeConfig(); + } + catch (RemoteException e) { + retVal = null; + } + + return retVal; + } + + /** + * Release all native resources held by this {@link android.os.CommonTimeConfig} instance. Once + * resources have been released, the {@link android.os.CommonTimeConfig} instance is + * disconnected from the native service and will throw a {@link android.os.RemoteException} if + * any of its methods are called. Clients should always call release on their client instances + * before releasing their last Java reference to the instance. Failure to do this will cause + * non-deterministic native resource reclamation and may cause the common time service to remain + * active on the network for longer than it should. + */ + public void release() { + if (null != mRemote) { + try { + mRemote.unlinkToDeath(mDeathHandler, 0); + } + catch (NoSuchElementException e) { } + mRemote = null; + } + mUtils = null; + } + + /** + * Gets the current priority of the common time service used in the master election protocol. + * + * @return an 8 bit value indicating the priority of this common time service relative to other + * common time services operating in the same domain. + * @throws android.os.RemoteException + */ + public byte getMasterElectionPriority() + throws RemoteException { + throwOnDeadServer(); + return (byte)mUtils.transactGetInt(METHOD_GET_MASTER_ELECTION_PRIORITY, -1); + } + + /** + * Sets the current priority of the common time service used in the master election protocol. + * + * @param priority priority of the common time service used in the master election protocol. + * Lower numbers are lower priority. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterElectionPriority(byte priority) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_MASTER_ELECTION_PRIORITY, priority); + } + + /** + * Gets the IP endpoint used by the time service to participate in the master election protocol. + * + * @return an InetSocketAddress containing the IP address and UDP port being used by the + * system's common time service to participate in the master election protocol. + * @throws android.os.RemoteException + */ + public InetSocketAddress getMasterElectionEndpoint() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ELECTION_ENDPOINT); + } + + /** + * Sets the IP endpoint used by the common time service to participate in the master election + * protocol. + * + * @param ep The IP address and UDP port to be used by the common time service to participate in + * the master election protocol. The supplied IP address must be either the broadcast or + * multicast address, unicast addresses are considered to be illegal values. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterElectionEndpoint(InetSocketAddress ep) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetSockaddr(METHOD_SET_MASTER_ELECTION_ENDPOINT, ep); + } + + /** + * Gets the current group ID used by the common time service in the master election protocol. + * + * @return The 64-bit group ID of the common time service. + * @throws android.os.RemoteException + */ + public long getMasterElectionGroupId() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetLong(METHOD_GET_MASTER_ELECTION_GROUP_ID, INVALID_GROUP_ID); + } + + /** + * Sets the current group ID used by the common time service in the master election protocol. + * + * @param id The 64-bit group ID of the common time service. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterElectionGroupId(long id) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetLong(METHOD_SET_MASTER_ELECTION_GROUP_ID, id); + } + + /** + * Gets the name of the network interface which the common time service attempts to bind to. + * + * @return a string with the network interface name which the common time service is bound to, + * or null if the service is currently unbound. Examples of interface names are things like + * "eth0", or "wlan0". + * @throws android.os.RemoteException + */ + public String getInterfaceBinding() + throws RemoteException { + throwOnDeadServer(); + + String ifaceName = mUtils.transactGetString(METHOD_GET_INTERFACE_BINDING, null); + + if ((null != ifaceName) && (0 == ifaceName.length())) + return null; + + return ifaceName; + } + + /** + * Sets the name of the network interface which the common time service should attempt to bind + * to. + * + * @param ifaceName The name of the network interface ("eth0", "wlan0", etc...) wich the common + * time service should attempt to bind to, or null to force the common time service to unbind + * from the network and run in networkless mode. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setNetworkBinding(String ifaceName) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + + return mUtils.transactSetString(METHOD_SET_INTERFACE_BINDING, + (null == ifaceName) ? "" : ifaceName); + } + + /** + * Gets the amount of time the common time service will wait between master announcements when + * it is the timeline master. + * + * @return The time (in milliseconds) between master announcements. + * @throws android.os.RemoteException + */ + public int getMasterAnnounceInterval() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_MASTER_ANNOUNCE_INTERVAL, -1); + } + + /** + * Sets the amount of time the common time service will wait between master announcements when + * it is the timeline master. + * + * @param interval The time (in milliseconds) between master announcements. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterAnnounceInterval(int interval) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_MASTER_ANNOUNCE_INTERVAL, interval); + } + + /** + * Gets the amount of time the common time service will wait between time synchronization + * requests when it is the client of another common time service on the network. + * + * @return The time (in milliseconds) between time sync requests. + * @throws android.os.RemoteException + */ + public int getClientSyncInterval() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_CLIENT_SYNC_INTERVAL, -1); + } + + /** + * Sets the amount of time the common time service will wait between time synchronization + * requests when it is the client of another common time service on the network. + * + * @param interval The time (in milliseconds) between time sync requests. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setClientSyncInterval(int interval) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_CLIENT_SYNC_INTERVAL, interval); + } + + /** + * Gets the panic threshold for the estimated error level of the common time service. When the + * common time service's estimated error rises above this level, the service will panic and + * reset, causing a discontinuity in the currently synchronized timeline. + * + * @return The threshold (in microseconds) past which the common time service will panic. + * @throws android.os.RemoteException + */ + public int getPanicThreshold() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_PANIC_THRESHOLD, -1); + } + + /** + * Sets the panic threshold for the estimated error level of the common time service. When the + * common time service's estimated error rises above this level, the service will panic and + * reset, causing a discontinuity in the currently synchronized timeline. + * + * @param threshold The threshold (in microseconds) past which the common time service will + * panic. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setPanicThreshold(int threshold) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_PANIC_THRESHOLD, threshold); + } + + /** + * Gets the current state of the common time service's auto disable flag. + * + * @return The current state of the common time service's auto disable flag. + * @throws android.os.RemoteException + */ + public boolean getAutoDisable() + throws RemoteException { + throwOnDeadServer(); + return (1 == mUtils.transactGetInt(METHOD_GET_AUTO_DISABLE, 1)); + } + + /** + * Sets the current state of the common time service's auto disable flag. When the time + * service's auto disable flag is set, it will automatically cease all network activity when + * it has no active local clients, resuming activity the next time the service has interested + * local clients. When the auto disabled flag is cleared, the common time service will continue + * to participate the time synchronization group even when it has no active local clients. + * + * @param autoDisable The desired state of the common time service's auto disable flag. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setAutoDisable(boolean autoDisable) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + + return mUtils.transactSetInt(METHOD_SET_AUTO_DISABLE, autoDisable ? 1 : 0); + } + + /** + * At startup, the time service enters the initial state and remains there until it is given a + * network interface to bind to. Common time will be unavailable to clients of the common time + * service until the service joins a network (even an empty network). Devices may use the + * {@link #forceNetworklessMasterMode()} method to force a time service in the INITIAL state + * with no network configuration to assume MASTER status for a brand new timeline in order to + * allow clients of the common time service to operate, even though the device is isolated and + * not on any network. When a networkless master does join a network, it will defer to any + * masters already on the network, or continue to maintain the timeline it made up during its + * networkless state if no other masters are detected. Attempting to force a client into master + * mode while it is actively bound to a network will fail with the status code {@link #ERROR} + * + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int forceNetworklessMasterMode() { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(METHOD_FORCE_NETWORKLESS_MASTER_MODE, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + /** + * The OnServerDiedListener interface defines a method called by the + * {@link android.os.CommonTimeConfig} instance to indicate that the connection to the native + * media server has been broken and that the {@link android.os.CommonTimeConfig} instance will + * need to be released and re-created. The client application can implement this interface and + * register the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method. + */ + public interface OnServerDiedListener { + /** + * Method called when the native common time service has died. <p>If the native common time + * service encounters a fatal error and needs to restart, the binder connection from the + * {@link android.os.CommonTimeConfig} instance to the common time service will be broken. + */ + void onServerDied(); + } + + /** + * Registers an OnServerDiedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + */ + public void setServerDiedListener(OnServerDiedListener listener) { + synchronized (mListenerLock) { + mServerDiedListener = listener; + } + } + + protected void finalize() throws Throwable { release(); } + + private boolean checkDeadServer() { + return ((null == mRemote) || (null == mUtils)); + } + + private void throwOnDeadServer() throws RemoteException { + if (checkDeadServer()) + throw new RemoteException(); + } + + private final Object mListenerLock = new Object(); + private OnServerDiedListener mServerDiedListener = null; + + private IBinder mRemote = null; + private String mInterfaceDesc = ""; + private CommonTimeUtils mUtils; + + private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() { + public void binderDied() { + synchronized (mListenerLock) { + if (null != mServerDiedListener) + mServerDiedListener.onServerDied(); + } + } + }; + + private static final int METHOD_GET_MASTER_ELECTION_PRIORITY = IBinder.FIRST_CALL_TRANSACTION; + private static final int METHOD_SET_MASTER_ELECTION_PRIORITY = METHOD_GET_MASTER_ELECTION_PRIORITY + 1; + private static final int METHOD_GET_MASTER_ELECTION_ENDPOINT = METHOD_SET_MASTER_ELECTION_PRIORITY + 1; + private static final int METHOD_SET_MASTER_ELECTION_ENDPOINT = METHOD_GET_MASTER_ELECTION_ENDPOINT + 1; + private static final int METHOD_GET_MASTER_ELECTION_GROUP_ID = METHOD_SET_MASTER_ELECTION_ENDPOINT + 1; + private static final int METHOD_SET_MASTER_ELECTION_GROUP_ID = METHOD_GET_MASTER_ELECTION_GROUP_ID + 1; + private static final int METHOD_GET_INTERFACE_BINDING = METHOD_SET_MASTER_ELECTION_GROUP_ID + 1; + private static final int METHOD_SET_INTERFACE_BINDING = METHOD_GET_INTERFACE_BINDING + 1; + private static final int METHOD_GET_MASTER_ANNOUNCE_INTERVAL = METHOD_SET_INTERFACE_BINDING + 1; + private static final int METHOD_SET_MASTER_ANNOUNCE_INTERVAL = METHOD_GET_MASTER_ANNOUNCE_INTERVAL + 1; + private static final int METHOD_GET_CLIENT_SYNC_INTERVAL = METHOD_SET_MASTER_ANNOUNCE_INTERVAL + 1; + private static final int METHOD_SET_CLIENT_SYNC_INTERVAL = METHOD_GET_CLIENT_SYNC_INTERVAL + 1; + private static final int METHOD_GET_PANIC_THRESHOLD = METHOD_SET_CLIENT_SYNC_INTERVAL + 1; + private static final int METHOD_SET_PANIC_THRESHOLD = METHOD_GET_PANIC_THRESHOLD + 1; + private static final int METHOD_GET_AUTO_DISABLE = METHOD_SET_PANIC_THRESHOLD + 1; + private static final int METHOD_SET_AUTO_DISABLE = METHOD_GET_AUTO_DISABLE + 1; + private static final int METHOD_FORCE_NETWORKLESS_MASTER_MODE = METHOD_SET_AUTO_DISABLE + 1; +} diff --git a/core/java/android/os/IUpdateLock.aidl b/core/java/android/os/IUpdateLock.aidl new file mode 100644 index 0000000..4492fb8 --- /dev/null +++ b/core/java/android/os/IUpdateLock.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 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.os; + +/** + * Direct interface to the UpdateLockService's functionality + * + * {@hide} + */ +interface IUpdateLock { + void acquireUpdateLock(IBinder token, String tag); + void releaseUpdateLock(IBinder token); +} diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 73e8d98..43cf74e 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; import java.security.GeneralSecurityException; import java.security.PublicKey; @@ -103,7 +104,12 @@ public class RecoverySystem { Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); - trusted.add(cf.generateCertificate(zip.getInputStream(entry))); + InputStream is = zip.getInputStream(entry); + try { + trusted.add(cf.generateCertificate(is)); + } finally { + is.close(); + } } } finally { zip.close(); @@ -162,8 +168,6 @@ public class RecoverySystem { int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8); int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8); - Log.v(TAG, String.format("comment size %d; signature start %d", - commentSize, signatureStart)); byte[] eocd = new byte[commentSize + 22]; raf.seek(fileLen - (commentSize + 22)); diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java index ac3cc92..9b3a2d6 100755 --- a/core/java/android/os/TokenWatcher.java +++ b/core/java/android/os/TokenWatcher.java @@ -16,6 +16,8 @@ package android.os; +import java.io.PrintWriter; +import java.util.ArrayList; import java.util.WeakHashMap; import java.util.Set; import android.util.Log; @@ -115,15 +117,31 @@ public abstract class TokenWatcher public void dump() { + ArrayList<String> a = dumpInternal(); + for (String s : a) { + Log.i(mTag, s); + } + } + + public void dump(PrintWriter pw) { + ArrayList<String> a = dumpInternal(); + for (String s : a) { + pw.println(s); + } + } + + private ArrayList<String> dumpInternal() { + ArrayList<String> a = new ArrayList<String>(); synchronized (mTokens) { Set<IBinder> keys = mTokens.keySet(); - Log.i(mTag, "Token count: " + mTokens.size()); + a.add("Token count: " + mTokens.size()); int i = 0; for (IBinder b: keys) { - Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b); + a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b); i++; } } + return a; } private Runnable mNotificationTask = new Runnable() { diff --git a/core/java/android/os/UpdateLock.java b/core/java/android/os/UpdateLock.java new file mode 100644 index 0000000..4060326 --- /dev/null +++ b/core/java/android/os/UpdateLock.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2012 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.os; + +import android.content.Context; +import android.util.Log; + +/** + * Advisory wakelock-like mechanism by which processes that should not be interrupted for + * OTA/update purposes can so advise the OS. This is particularly relevant for headless + * or kiosk-like operation. + * + * @hide + */ +public class UpdateLock { + private static final boolean DEBUG = false; + private static final String TAG = "UpdateLock"; + + private static IUpdateLock sService; + private static void checkService() { + if (sService == null) { + sService = IUpdateLock.Stub.asInterface( + ServiceManager.getService(Context.UPDATE_LOCK_SERVICE)); + } + } + + IBinder mToken; + int mCount = 0; + boolean mRefCounted = true; + boolean mHeld = false; + final String mTag; + + /** + * Broadcast Intent action sent when the global update lock state changes, + * i.e. when the first locker acquires an update lock, or when the last + * locker releases theirs. The broadcast is sticky but is sent only to + * registered receivers. + */ + public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED"; + + /** + * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating + * whether now is an appropriate time to interrupt device activity with an + * update operation. True means that updates are okay right now; false indicates + * that perhaps later would be a better time. + */ + public static final String NOW_IS_CONVENIENT = "nowisconvenient"; + + /** + * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the + * wall-clock time [in UTC] at which the broadcast was sent. Note that this is + * in the System.currentTimeMillis() time base, which may be non-monotonic especially + * around reboots. + */ + public static final String TIMESTAMP = "timestamp"; + + /** + * Construct an UpdateLock instance. + * @param tag An arbitrary string used to identify this lock instance in dump output. + */ + public UpdateLock(String tag) { + mTag = tag; + mToken = new Binder(); + } + + /** + * Change the refcount behavior of this update lock. + */ + public void setReferenceCounted(boolean isRefCounted) { + if (DEBUG) { + Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this); + } + mRefCounted = isRefCounted; + } + + /** + * Is this lock currently held? + */ + public boolean isHeld() { + synchronized (mToken) { + return mHeld; + } + } + + /** + * Acquire an update lock. + */ + public void acquire() { + if (DEBUG) { + Log.v(TAG, "acquire() : " + this, new RuntimeException("here")); + } + checkService(); + synchronized (mToken) { + acquireLocked(); + } + } + + private void acquireLocked() { + if (!mRefCounted || mCount++ == 0) { + if (sService != null) { + try { + sService.acquireUpdateLock(mToken, mTag); + } catch (RemoteException e) { + Log.e(TAG, "Unable to contact service to acquire"); + } + } + mHeld = true; + } + } + + /** + * Release this update lock. + */ + public void release() { + if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here")); + checkService(); + synchronized (mToken) { + releaseLocked(); + } + } + + private void releaseLocked() { + if (!mRefCounted || --mCount == 0) { + if (sService != null) { + try { + sService.releaseUpdateLock(mToken); + } catch (RemoteException e) { + Log.e(TAG, "Unable to contact service to release"); + } + } + mHeld = false; + } + if (mCount < 0) { + throw new RuntimeException("UpdateLock under-locked"); + } + } + + @Override + protected void finalize() throws Throwable { + synchronized (mToken) { + // if mHeld is true, sService must be non-null + if (mHeld) { + Log.wtf(TAG, "UpdateLock finalized while still held"); + try { + sService.releaseUpdateLock(mToken); + } catch (RemoteException e) { + Log.e(TAG, "Unable to contact service to release"); + } + } + } + } +} diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index cdb622c..fbf512c 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,6 +16,7 @@ package android.os.storage; +import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -534,6 +535,7 @@ public class StorageManager * @hide */ public String getVolumeState(String mountPoint) { + if (mMountService == null) return Environment.MEDIA_REMOVED; try { return mMountService.getVolumeState(mountPoint); } catch (RemoteException e) { @@ -547,6 +549,7 @@ public class StorageManager * @hide */ public StorageVolume[] getVolumeList() { + if (mMountService == null) return new StorageVolume[0]; try { Parcelable[] list = mMountService.getVolumeList(); if (list == null) return new StorageVolume[0]; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d7fab37..f0a52d7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1473,13 +1473,28 @@ public final class Settings { public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco"; /** - * Whether the notifications should use the ring volume (value of 1) or a separate - * notification volume (value of 0). In most cases, users will have this enabled so the - * notification and ringer volumes will be the same. However, power users can disable this - * and use the separate notification volume control. + * Master volume (float in the range 0.0f to 1.0f). + * @hide + */ + public static final String VOLUME_MASTER = "volume_master"; + + /** + * Master volume mute (int 1 = mute, 0 = not muted). + * + * @hide + */ + public static final String VOLUME_MASTER_MUTE = "volume_master_mute"; + + /** + * Whether the notifications should use the ring volume (value of 1) or + * a separate notification volume (value of 0). In most cases, users + * will have this enabled so the notification and ringer volumes will be + * the same. However, power users can disable this and use the separate + * notification volume control. * <p> - * Note: This is a one-off setting that will be removed in the future when there is profile - * support. For this reason, it is kept hidden from the public APIs. + * Note: This is a one-off setting that will be removed in the future + * when there is profile support. For this reason, it is kept hidden + * from the public APIs. * * @hide * @deprecated @@ -1797,6 +1812,12 @@ public final class Settings { public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled"; /** + * Whether the lockscreen should be completely disabled. + * @hide + */ + public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled"; + + /** * URI for the low battery sound file. * @hide */ diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index fecc8f9..dce31db 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -46,6 +46,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.res.Resources; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -554,12 +555,15 @@ public class BluetoothService extends IBluetooth.Stub { private synchronized void updateSdpRecords() { ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(); + Resources R = mContext.getResources(); + // Add the default records - uuids.add(BluetoothUuid.HSP_AG); - uuids.add(BluetoothUuid.ObexObjectPush); + if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) { + uuids.add(BluetoothUuid.HSP_AG); + uuids.add(BluetoothUuid.ObexObjectPush); + } - if (mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_voice_capable)) { + if (R.getBoolean(com.android.internal.R.bool.config_voice_capable)) { uuids.add(BluetoothUuid.Handsfree_AG); uuids.add(BluetoothUuid.PBAP_PSE); } @@ -567,14 +571,16 @@ public class BluetoothService extends IBluetooth.Stub { // Add SDP records for profiles maintained by Android userspace addReservedSdpRecords(uuids); - // Enable profiles maintained by Bluez userspace. - setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE, - BluetoothPanProfileHandler.NAP_BRIDGE); + if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) { + // Enable profiles maintained by Bluez userspace. + setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE, + BluetoothPanProfileHandler.NAP_BRIDGE); - // Add SDP records for profiles maintained by Bluez userspace - uuids.add(BluetoothUuid.AudioSource); - uuids.add(BluetoothUuid.AvrcpTarget); - uuids.add(BluetoothUuid.NAP); + // Add SDP records for profiles maintained by Bluez userspace + uuids.add(BluetoothUuid.AudioSource); + uuids.add(BluetoothUuid.AvrcpTarget); + uuids.add(BluetoothUuid.NAP); + } // Cannot cast uuids.toArray directly since ParcelUuid is parcelable mAdapterUuids = new ParcelUuid[uuids.size()]; @@ -1743,6 +1749,7 @@ public class BluetoothService extends IBluetooth.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) { + pw.println("state: " + getBluetoothStateInternal()); return; } diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index 48fe0df..b6e37a6 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -91,6 +91,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie private static final int MSG_VIBRATE = 4; private static final int MSG_TIMEOUT = 5; private static final int MSG_RINGER_MODE_CHANGED = 6; + private static final int MSG_MUTE_CHANGED = 7; + + // Pseudo stream type for master volume + private static final int STREAM_MASTER = -100; protected Context mContext; private AudioManager mAudioManager; @@ -148,7 +152,13 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie R.string.volume_icon_description_notification, R.drawable.ic_audio_notification, R.drawable.ic_audio_notification_mute, - true); + true), + // for now, use media resources for master volume + MasterStream(STREAM_MASTER, + R.string.volume_icon_description_media, + R.drawable.ic_audio_vol, + R.drawable.ic_audio_vol_mute, + false); int streamType; int descRes; @@ -173,7 +183,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie StreamResources.VoiceStream, StreamResources.MediaStream, StreamResources.NotificationStream, - StreamResources.AlarmStream + StreamResources.AlarmStream, + StreamResources.MasterStream }; /** Object that contains data for each slider */ @@ -195,6 +206,16 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioService = volumeService; + // For now, only show master volume if master volume is supported + boolean useMasterVolume = context.getResources().getBoolean( + com.android.internal.R.bool.config_useMasterVolume); + if (useMasterVolume) { + for (int i = 0; i < STREAMS.length; i++) { + StreamResources streamRes = STREAMS[i]; + streamRes.show = (streamRes.streamType == STREAM_MASTER); + } + } + LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = mView = inflater.inflate(R.layout.volume_adjust, null); @@ -245,7 +266,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie mVibrator = new Vibrator(); mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable); - mShowCombinedVolumes = !mVoiceCapable; + mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume; // If we don't want to show multiple volumes, hide the settings button and divider if (!mShowCombinedVolumes) { mMoreButton.setVisibility(View.GONE); @@ -274,7 +295,43 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie } private boolean isMuted(int streamType) { - return mAudioManager.isStreamMute(streamType); + if (streamType == STREAM_MASTER) { + return mAudioService.isMasterMute(); + } else { + return mAudioService.isStreamMute(streamType); + } + } + + private int getStreamMaxVolume(int streamType) { + if (streamType == STREAM_MASTER) { + return mAudioService.getMasterMaxVolume(); + } else { + return mAudioService.getStreamMaxVolume(streamType); + } + } + + private int getStreamVolume(int streamType) { + if (streamType == STREAM_MASTER) { + return mAudioService.getMasterVolume(); + } else { + return mAudioService.getStreamVolume(streamType); + } + } + + private void setStreamVolume(int streamType, int index, int flags) { + if (streamType == STREAM_MASTER) { + mAudioService.setMasterVolume(index, flags); + } else { + mAudioService.setStreamVolume(streamType, index, flags); + } + } + + private int getLastAudibleStreamVolume(int streamType) { + if (streamType == STREAM_MASTER) { + return mAudioService.getLastAudibleMasterVolume(); + } else { + return mAudioService.getLastAudibleStreamVolume(streamType); + } } private void createSliders() { @@ -301,7 +358,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar); int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO || streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0; - sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(streamType) + plusOne); + sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne); sc.seekbarView.setOnSeekBarChangeListener(this); sc.seekbarView.setTag(sc); mStreamControls.put(streamType, sc); @@ -342,7 +399,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie /** Update the mute and progress state of a slider */ private void updateSlider(StreamControl sc) { - sc.seekbarView.setProgress(mAudioManager.getLastAudibleStreamVolume(sc.streamType)); + sc.seekbarView.setProgress(getLastAudibleStreamVolume(sc.streamType)); final boolean muted = isMuted(sc.streamType); sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes); if (sc.streamType == AudioManager.STREAM_RING && muted @@ -390,6 +447,23 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget(); } + public void postMasterVolumeChanged(int flags) { + postVolumeChanged(STREAM_MASTER, flags); + } + + public void postMuteChanged(int streamType, int flags) { + if (hasMessages(MSG_VOLUME_CHANGED)) return; + if (mStreamControls == null) { + createSliders(); + } + removeMessages(MSG_FREE_RESOURCES); + obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget(); + } + + public void postMasterMuteChanged(int flags) { + postMuteChanged(STREAM_MASTER, flags); + } + /** * Override this if you have other work to do when the volume changes (for * example, vibrating, playing a sound, etc.). Make sure to call through to @@ -423,10 +497,22 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie resetTimeout(); } + protected void onMuteChanged(int streamType, int flags) { + + if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")"); + + StreamControl sc = mStreamControls.get(streamType); + if (sc != null) { + sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes); + } + + onVolumeChanged(streamType, flags); + } + protected void onShowVolumeChanged(int streamType, int flags) { - int index = mAudioService.isStreamMute(streamType) ? - mAudioService.getLastAudibleStreamVolume(streamType) - : mAudioService.getStreamVolume(streamType); + int index = isMuted(streamType) ? + getLastAudibleStreamVolume(streamType) + : getStreamVolume(streamType); mRingIsSilent = false; @@ -437,7 +523,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie // get max volume for progress bar - int max = mAudioService.getStreamMaxVolume(streamType); + int max = getStreamMaxVolume(streamType); switch (streamType) { @@ -571,6 +657,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie * Lock on this VolumePanel instance as long as you use the returned ToneGenerator. */ private ToneGenerator getOrCreateToneGenerator(int streamType) { + if (streamType == STREAM_MASTER) return null; synchronized (this) { if (mToneGenerators[streamType] == null) { try { @@ -620,6 +707,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie break; } + case MSG_MUTE_CHANGED: { + onMuteChanged(msg.arg1, msg.arg2); + break; + } + case MSG_FREE_RESOURCES: { onFreeResources(); break; @@ -671,8 +763,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie final Object tag = seekBar.getTag(); if (fromUser && tag instanceof StreamControl) { StreamControl sc = (StreamControl) tag; - if (mAudioManager.getStreamVolume(sc.streamType) != progress) { - mAudioManager.setStreamVolume(sc.streamType, progress, 0); + if (getStreamVolume(sc.streamType) != progress) { + setStreamVolume(sc.streamType, progress, 0); } } resetTimeout(); |
