diff options
Diffstat (limited to 'core/java')
38 files changed, 1518 insertions, 382 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 2df35df..eadf5e9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -80,6 +80,8 @@ import android.media.projection.MediaProjectionManager; import android.media.session.MediaSessionManager; import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; +import android.midi.IMidiManager; +import android.midi.MidiManager; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.EthernetManager; @@ -92,6 +94,8 @@ import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; import android.net.wifi.IWifiManager; import android.net.wifi.WifiManager; +import android.net.wifi.passpoint.IWifiPasspointManager; +import android.net.wifi.passpoint.WifiPasspointManager; import android.net.wifi.p2p.IWifiP2pManager; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.IWifiScanner; @@ -603,6 +607,13 @@ class ContextImpl extends Context { return new WifiManager(ctx.getOuterContext(), service); }}); + registerService(WIFI_PASSPOINT_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(WIFI_PASSPOINT_SERVICE); + IWifiPasspointManager service = IWifiPasspointManager.Stub.asInterface(b); + return new WifiPasspointManager(ctx.getOuterContext(), service); + }}); + registerService(WIFI_P2P_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(WIFI_P2P_SERVICE); @@ -768,6 +779,12 @@ class ContextImpl extends Context { IBinder b = ServiceManager.getService(APPWIDGET_SERVICE); return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b)); }}); + + registerService(MIDI_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(MIDI_SERVICE); + return new MidiManager(ctx, IMidiManager.Stub.asInterface(b)); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ead89b3..df6cc73 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -52,11 +52,15 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; +import java.security.KeyFactory; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -1934,13 +1938,15 @@ public class DevicePolicyManager { String alias) { try { final byte[] pemCert = Credentials.convertToPem(cert); - return mService.installKeyPair(who, privKey.getEncoded(), pemCert, alias); - } catch (CertificateException e) { - Log.w(TAG, "Error encoding certificate", e); - } catch (IOException e) { - Log.w(TAG, "Error writing certificate", e); + final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm()) + .getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded(); + return mService.installKeyPair(who, pkcs8Key, pemCert, alias); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + Log.w(TAG, "Failed to obtain private key material", e); + } catch (CertificateException | IOException e) { + Log.w(TAG, "Could not pem-encode certificate", e); } return false; } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java index a15bd97..7b5a045 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -61,6 +61,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { */ public static final int CALL_STATE_TERMINATED = 7; + private final BluetoothDevice mDevice; private final int mId; private int mState; private String mNumber; @@ -70,8 +71,9 @@ public final class BluetoothHeadsetClientCall implements Parcelable { /** * Creates BluetoothHeadsetClientCall instance. */ - public BluetoothHeadsetClientCall(int id, int state, String number, boolean multiParty, - boolean outgoing) { + public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, + boolean multiParty, boolean outgoing) { + mDevice = device; mId = id; mState = state; mNumber = number != null ? number : ""; @@ -114,6 +116,15 @@ public final class BluetoothHeadsetClientCall implements Parcelable { } /** + * Gets call's device. + * + * @return call device. + */ + public BluetoothDevice getDevice() { + return mDevice; + } + + /** * Gets call's Id. * * @return call id. @@ -161,7 +172,9 @@ public final class BluetoothHeadsetClientCall implements Parcelable { } public String toString() { - StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mId: "); + StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); + builder.append(mDevice); + builder.append(", mId: "); builder.append(mId); builder.append(", mState: "); switch (mState) { @@ -192,8 +205,9 @@ public final class BluetoothHeadsetClientCall implements Parcelable { new Parcelable.Creator<BluetoothHeadsetClientCall>() { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { - return new BluetoothHeadsetClientCall(in.readInt(), in.readInt(), - in.readString(), in.readInt() == 1, in.readInt() == 1); + return new BluetoothHeadsetClientCall((BluetoothDevice)in.readParcelable(null), + in.readInt(), in.readInt(), in.readString(), + in.readInt() == 1, in.readInt() == 1); } @Override @@ -204,6 +218,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mDevice, 0); out.writeInt(mId); out.writeInt(mState); out.writeString(mNumber); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e6bb09f..015e1ec 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -146,12 +146,13 @@ public abstract class Context { @IntDef(flag = true, value = { BIND_AUTO_CREATE, - BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, - BIND_WAIVE_PRIORITY + BIND_WAIVE_PRIORITY, + BIND_IMPORTANT, + BIND_ADJUST_WITH_ACTIVITY }) @Retention(RetentionPolicy.SOURCE) public @interface BindServiceFlags {} @@ -2142,6 +2143,7 @@ public abstract class Context { MEDIA_SESSION_SERVICE, BATTERY_SERVICE, JOB_SCHEDULER_SERVICE, + MIDI_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -2915,6 +2917,15 @@ public abstract class Context { public static final String MEDIA_PROJECTION_SERVICE = "media_projection"; /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.midi.MidiManager} for accessing the MIDI service. + * + * @see #getSystemService + * @hide + */ + public static final String MIDI_SERVICE = "midi"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/hardware/camera2/params/LensShadingMap.java b/core/java/android/hardware/camera2/params/LensShadingMap.java index 9bbc33a..13929b1 100644 --- a/core/java/android/hardware/camera2/params/LensShadingMap.java +++ b/core/java/android/hardware/camera2/params/LensShadingMap.java @@ -238,6 +238,51 @@ public final class LensShadingMap { return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash); } + /** + * Return the LensShadingMap as a string representation. + * + * <p> {@code "LensShadingMap{R:([%f, %f, ... %f], ... [%f, %f, ... %f]), G_even:([%f, %f, ... + * %f], ... [%f, %f, ... %f]), G_odd:([%f, %f, ... %f], ... [%f, %f, ... %f]), B:([%f, %f, ... + * %f], ... [%f, %f, ... %f])}"}, + * where each {@code %f} represents one gain factor and each {@code [%f, %f, ... %f]} represents + * a row of the lens shading map</p> + * + * @return string representation of {@link LensShadingMap} + */ + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("LensShadingMap{"); + + final String channelPrefix[] = {"R:(", "G_even:(", "G_odd:(", "B:("}; + + for (int ch = 0; ch < COUNT; ch++) { + str.append(channelPrefix[ch]); + + for (int r = 0; r < mRows; r++) { + str.append("["); + for (int c = 0; c < mColumns; c++) { + float gain = getGainFactor(ch, c, r); + str.append(gain); + if (c < mColumns - 1) { + str.append(", "); + } + } + str.append("]"); + if (r < mRows - 1) { + str.append(", "); + } + } + + str.append(")"); + if (ch < COUNT - 1) { + str.append(", "); + } + } + + str.append("}"); + return str.toString(); + } private final int mRows; private final int mColumns; diff --git a/core/java/android/midi/IMidiListener.aidl b/core/java/android/midi/IMidiListener.aidl new file mode 100644 index 0000000..b650593 --- /dev/null +++ b/core/java/android/midi/IMidiListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 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.midi; + +import android.midi.MidiDeviceInfo; + +/** @hide */ +oneway interface IMidiListener +{ + void onDeviceAdded(in MidiDeviceInfo device); + void onDeviceRemoved(in MidiDeviceInfo device); +} diff --git a/core/java/android/midi/IMidiManager.aidl b/core/java/android/midi/IMidiManager.aidl new file mode 100644 index 0000000..7a9f887 --- /dev/null +++ b/core/java/android/midi/IMidiManager.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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.midi; + +import android.hardware.usb.UsbDevice; +import android.midi.IMidiListener; +import android.midi.MidiDevice; +import android.midi.MidiDeviceInfo; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; + +/** @hide */ +interface IMidiManager +{ + MidiDeviceInfo[] getDeviceList(); + + // for device creation & removal notifications + void registerListener(IBinder token, in IMidiListener listener); + void unregisterListener(IBinder token, in IMidiListener listener); + + // for communicating with MIDI devices + ParcelFileDescriptor openDevice(IBinder token, in MidiDeviceInfo device); + + // for implementing virtual MIDI devices + MidiDevice registerVirtualDevice(IBinder token, int numInputPorts, int numOutputPorts, + in Bundle properties); + void unregisterVirtualDevice(IBinder token, in MidiDeviceInfo device); + + // for use by UsbAudioManager + void alsaDeviceAdded(int card, int device, in UsbDevice usbDevice); + void alsaDeviceRemoved(in UsbDevice usbDevice); +} diff --git a/core/java/android/midi/MidiDevice.aidl b/core/java/android/midi/MidiDevice.aidl new file mode 100644 index 0000000..11bb497 --- /dev/null +++ b/core/java/android/midi/MidiDevice.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014, 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.midi; + +parcelable MidiDevice; diff --git a/core/java/android/midi/MidiDevice.java b/core/java/android/midi/MidiDevice.java new file mode 100644 index 0000000..e704ea0 --- /dev/null +++ b/core/java/android/midi/MidiDevice.java @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2014 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.midi; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * This class is used for sending and receiving data to and from an MIDI device + * Instances of this class are created by {@link MidiManager#openDevice}. + * This class can also be used to provide the implementation for a virtual device. + * + * This class implements Parcelable so it can be returned from MidiService when creating + * virtual MIDI devices. + * + * @hide + */ +public final class MidiDevice implements Parcelable { + private static final String TAG = "MidiDevice"; + + private final MidiDeviceInfo mDeviceInfo; + private ParcelFileDescriptor mParcelFileDescriptor; + private FileInputStream mInputStream; + private FileOutputStream mOutputStream; + + // lazily populated lists of ports + private final MidiInputPort[] mInputPorts; + private final MidiOutputPort[] mOutputPorts; + + // array of receiver lists, indexed by port number + private final ArrayList<MidiReceiver>[] mReceivers; + + private int mReceiverCount; // total number of receivers for all ports + + /** + * Minimum size of packed message as sent through our ParcelFileDescriptor + * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message + * @hide + */ + public static final int MIN_PACKED_MESSAGE_SIZE = 10; + + /** + * Maximum size of packed message as sent through our ParcelFileDescriptor + * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message + * @hide + */ + public static final int MAX_PACKED_MESSAGE_SIZE = 12; + + // This thread reads MIDI events from a socket and distributes them to the list of + // MidiReceivers attached to this device. + private final Thread mThread = new Thread() { + @Override + public void run() { + byte[] buffer = new byte[MAX_PACKED_MESSAGE_SIZE]; + ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>(); + + try { + while (true) { + // read next event + int count = mInputStream.read(buffer); + if (count < MIN_PACKED_MESSAGE_SIZE || count > MAX_PACKED_MESSAGE_SIZE) { + Log.e(TAG, "Number of bytes read out of range: " + count); + break; + } + + int offset = getMessageOffset(buffer, count); + int size = getMessageSize(buffer, count); + long timestamp = getMessageTimeStamp(buffer, count); + int port = getMessagePortNumber(buffer, count); + + synchronized (mReceivers) { + ArrayList<MidiReceiver> receivers = mReceivers[port]; + if (receivers != null) { + for (int i = 0; i < receivers.size(); i++) { + MidiReceiver receiver = receivers.get(i); + try { + receivers.get(i).onPost(buffer, offset, size, timestamp); + } catch (IOException e) { + Log.e(TAG, "post failed"); + deadReceivers.add(receiver); + } + } + // remove any receivers that failed + if (deadReceivers.size() > 0) { + for (MidiReceiver receiver: deadReceivers) { + receivers.remove(receiver); + mReceiverCount--; + } + deadReceivers.clear(); + } + if (receivers.size() == 0) { + mReceivers[port] = null; + } + // exit if we have no receivers left + if (mReceiverCount == 0) { + break; + } + } + } + } + } catch (IOException e) { + Log.e(TAG, "read failed"); + } + } + }; + + /** + * MidiDevice should only be instantiated by MidiManager or MidiService + * @hide + */ + public MidiDevice(MidiDeviceInfo deviceInfo, ParcelFileDescriptor pfd) { + mDeviceInfo = deviceInfo; + mParcelFileDescriptor = pfd; + int inputPorts = deviceInfo.getInputPortCount(); + int outputPorts = deviceInfo.getOutputPortCount(); + mInputPorts = new MidiInputPort[inputPorts]; + mOutputPorts = new MidiOutputPort[outputPorts]; + mReceivers = new ArrayList[outputPorts]; + } + + public MidiInputPort openInputPort(int portNumber) { + if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) { + throw new IllegalArgumentException("input port number out of range"); + } + synchronized (mInputPorts) { + if (mInputPorts[portNumber] == null) { + mInputPorts[portNumber] = new MidiInputPort(mOutputStream, portNumber); + } + return mInputPorts[portNumber]; + } + } + + public MidiOutputPort openOutputPort(int portNumber) { + if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) { + throw new IllegalArgumentException("output port number out of range"); + } + synchronized (mOutputPorts) { + if (mOutputPorts[portNumber] == null) { + mOutputPorts[portNumber] = new MidiOutputPort(this, portNumber); + } + return mOutputPorts[portNumber]; + } + } + + /* package */ void connect(MidiReceiver receiver, int portNumber) { + synchronized (mReceivers) { + if (mReceivers[portNumber] == null) { + mReceivers[portNumber] = new ArrayList<MidiReceiver>(); + } + mReceivers[portNumber].add(receiver); + if (mReceiverCount++ == 0) { + mThread.start(); + } + } + } + + /* package */ void disconnect(MidiReceiver receiver, int portNumber) { + synchronized (mReceivers) { + ArrayList<MidiReceiver> receivers = mReceivers[portNumber]; + if (receivers != null && receivers.remove(receiver)) { + mReceiverCount--; + } + } + } + + /* package */ boolean open() { + FileDescriptor fd = mParcelFileDescriptor.getFileDescriptor(); + try { + mInputStream = new FileInputStream(fd); + } catch (Exception e) { + Log.e(TAG, "could not create mInputStream", e); + return false; + } + + try { + mOutputStream = new FileOutputStream(fd); + } catch (Exception e) { + Log.e(TAG, "could not create mOutputStream", e); + return false; + } + return true; + } + + void close() { + try { + if (mInputStream != null) { + mInputStream.close(); + } + if (mOutputStream != null) { + mOutputStream.close(); + } + mParcelFileDescriptor.close(); + } catch (IOException e) { + } + } + + // returns our MidiDeviceInfo object, which describes this device + public MidiDeviceInfo getInfo() { + return mDeviceInfo; + } + + @Override + public String toString() { + return ("MidiDevice: " + mDeviceInfo.toString() + " fd: " + mParcelFileDescriptor); + } + + public static final Parcelable.Creator<MidiDevice> CREATOR = + new Parcelable.Creator<MidiDevice>() { + public MidiDevice createFromParcel(Parcel in) { + MidiDeviceInfo deviceInfo = (MidiDeviceInfo)in.readParcelable(null); + ParcelFileDescriptor pfd = (ParcelFileDescriptor)in.readParcelable(null); + return new MidiDevice(deviceInfo, pfd); + } + + public MidiDevice[] newArray(int size) { + return new MidiDevice[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mDeviceInfo, flags); + parcel.writeParcelable(mParcelFileDescriptor, flags); + } + + /** + * Utility function for packing a MIDI message to be sent through our ParcelFileDescriptor + * + * message byte array contains variable length MIDI message. + * messageSize is size of variable length MIDI message + * timestamp is message timestamp to pack + * dest is buffer to pack into + * returns size of packed message + * + * @hide + */ + public static int packMessage(byte[] message, int offset, int size, long timestamp, + int portNumber, byte[] dest) { + // pack variable length message first + System.arraycopy(message, offset, dest, 0, size); + int destOffset = size; + // timestamp takes 8 bytes + for (int i = 0; i < 8; i++) { + dest[destOffset++] = (byte)timestamp; + timestamp >>= 8; + } + // portNumber is last + dest[destOffset++] = (byte)portNumber; + + return destOffset; + } + + /** + * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor + * returns the offet of of MIDI message in packed buffer + * + * @hide + */ + public static int getMessageOffset(byte[] buffer, int bufferLength) { + // message is at start of buffer + return 0; + } + + /** + * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor + * returns size of MIDI message in packed buffer + * + * @hide + */ + public static int getMessageSize(byte[] buffer, int bufferLength) { + // message length is total buffer length minus size of the timestamp and port number + return bufferLength - 9 /* (sizeof(timestamp) + sizeof(portNumber)) */; + } + + /** + * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor + * unpacks timestamp from packed buffer + * + * @hide + */ + public static long getMessageTimeStamp(byte[] buffer, int bufferLength) { + long timestamp = 0; + + // timestamp follows variable length message data + int dataLength = getMessageSize(buffer, bufferLength); + for (int i = dataLength + 7; i >= dataLength; i--) { + // why can't Java deal with unsigned ints? + int b = buffer[i]; + if (b < 0) b += 256; + timestamp = (timestamp << 8) | b; + } + return timestamp; + } + + /** + * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor + * unpacks port number from packed buffer + * + * @hide + */ + public static int getMessagePortNumber(byte[] buffer, int bufferLength) { + // timestamp follows variable length message data and timestamp + int dataLength = getMessageSize(buffer, bufferLength); + return buffer[dataLength + 8 /* sizeof(timestamp) */]; + } +} diff --git a/core/java/android/midi/MidiDeviceInfo.aidl b/core/java/android/midi/MidiDeviceInfo.aidl new file mode 100644 index 0000000..59be059 --- /dev/null +++ b/core/java/android/midi/MidiDeviceInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014, 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.midi; + +parcelable MidiDeviceInfo; diff --git a/core/java/android/midi/MidiDeviceInfo.java b/core/java/android/midi/MidiDeviceInfo.java new file mode 100644 index 0000000..239481b --- /dev/null +++ b/core/java/android/midi/MidiDeviceInfo.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 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.midi; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class contains information to describe a MIDI device. + * For now we only have information that can be retrieved easily for USB devices, + * but we will probably expand this in the future. + * + * This class is just an immutable object to encapsulate the MIDI device description. + * Use the MidiDevice class to actually communicate with devices. + * + * @hide + */ +public class MidiDeviceInfo implements Parcelable { + + private static final String TAG = "MidiDeviceInfo"; + + public static final int TYPE_USB = 1; + public static final int TYPE_VIRTUAL = 2; + + private final int mType; // USB or virtual + private final int mId; // unique ID generated by MidiService + private final int mInputPortCount; + private final int mOutputPortCount; + private final Bundle mProperties; + + // used for USB devices only + private final int mAlsaCard; + private final int mAlsaDevice; + + /** + * Bundle key for the device's manufacturer name property. + * Used with the {@link android.os.Bundle} returned by {@link #getProperties}. + * Matches the USB device manufacturer name string for USB MIDI devices. + */ + public static final String PROPERTY_MANUFACTURER = "manufacturer"; + + /** + * Bundle key for the device's model name property. + * Used with the {@link android.os.Bundle} returned by {@link #getProperties} + * Matches the USB device product name string for USB MIDI devices. + */ + public static final String PROPERTY_MODEL = "model"; + + /** + * Bundle key for the device's serial number property. + * Used with the {@link android.os.Bundle} returned by {@link #getProperties} + * Matches the USB device serial number for USB MIDI devices. + */ + public static final String PROPERTY_SERIAL_NUMBER = "serial_number"; + + /** + * Bundle key for the device's {@link android.hardware.usb.UsbDevice}. + * Only set for USB MIDI devices. + * Used with the {@link android.os.Bundle} returned by {@link #getProperties} + */ + public static final String PROPERTY_USB_DEVICE = "usb_device"; + + /** + * MidiDeviceInfo should only be instantiated by MidiService implementation + * @hide + */ + public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, + Bundle properties) { + mType = type; + mId = id; + mInputPortCount = numInputPorts; + mOutputPortCount = numOutputPorts; + mProperties = properties; + mAlsaCard = -1; + mAlsaDevice = -1; + } + + /** + * MidiDeviceInfo should only be instantiated by MidiService implementation + * @hide + */ + public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, + Bundle properties, int alsaCard, int alsaDevice) { + mType = type; + mId = id; + mInputPortCount = numInputPorts; + mOutputPortCount = numOutputPorts; + mProperties = properties; + mAlsaCard = alsaCard; + mAlsaDevice = alsaDevice; + } + + /** + * Returns the type of the device. + * + * @return the device's type + */ + public int getType() { + return mType; + } + + /** + * Returns the ID of the device. + * This ID is generated by the MIDI service and is not persistent across device unplugs. + * + * @return the device's ID + */ + public int getId() { + return mId; + } + + /** + * Returns the device's number of input ports. + * + * @return the number of input ports + */ + public int getInputPortCount() { + return mInputPortCount; + } + + /** + * Returns the device's number of output ports. + * + * @return the number of output ports + */ + public int getOutputPortCount() { + return mOutputPortCount; + } + + /** + * Returns the {@link android.os.Bundle} containing the device's properties. + * + * @return the device's properties + */ + public Bundle getProperties() { + return mProperties; + } + + /** + * @hide + */ + public int getAlsaCard() { + return mAlsaCard; + } + + /** + * @hide + */ + public int getAlsaDevice() { + return mAlsaDevice; + } + + @Override + public boolean equals(Object o) { + if (o instanceof MidiDeviceInfo) { + return (((MidiDeviceInfo)o).mId == mId); + } else { + return false; + } + } + + @Override + public int hashCode() { + return mId; + } + + @Override + public String toString() { + return ("MidiDeviceInfo[mType=" + mType + + ",mInputPortCount=" + mInputPortCount + + ",mOutputPortCount=" + mOutputPortCount + + ",mProperties=" + mProperties + + ",mAlsaCard=" + mAlsaCard + + ",mAlsaDevice=" + mAlsaDevice); + } + + public static final Parcelable.Creator<MidiDeviceInfo> CREATOR = + new Parcelable.Creator<MidiDeviceInfo>() { + public MidiDeviceInfo createFromParcel(Parcel in) { + int type = in.readInt(); + int id = in.readInt(); + int inputPorts = in.readInt(); + int outputPorts = in.readInt(); + Bundle properties = in.readBundle(); + int card = in.readInt(); + int device = in.readInt(); + return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties, card, device); + } + + public MidiDeviceInfo[] newArray(int size) { + return new MidiDeviceInfo[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mType); + parcel.writeInt(mId); + parcel.writeInt(mInputPortCount); + parcel.writeInt(mOutputPortCount); + parcel.writeBundle(mProperties); + parcel.writeInt(mAlsaCard); + parcel.writeInt(mAlsaDevice); + } +} diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java new file mode 100644 index 0000000..583c367 --- /dev/null +++ b/core/java/android/midi/MidiInputPort.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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.midi; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * This class is used for sending data to a port on a MIDI device + * + * @hide + */ +public final class MidiInputPort extends MidiPort implements MidiReceiver { + + private final FileOutputStream mOutputStream; + // buffer to use for sending messages out our output stream + private final byte[] mBuffer = new byte[MidiDevice.MAX_PACKED_MESSAGE_SIZE]; + + /* package */ MidiInputPort(FileOutputStream outputStream, int portNumber) { + super(portNumber); + mOutputStream = outputStream; + } + + /** + * Writes a MIDI message to the input port + * + * @param msg message bytes + * @param offset offset of first byte of the message in msg array + * @param count size of the message in bytes + * @param timestamp future time to post the message + */ + public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException { + synchronized (mBuffer) { + int length = MidiDevice.packMessage(msg, offset, count, timestamp, mPortNumber, + mBuffer); + mOutputStream.write(mBuffer, 0, length); + } + } +} diff --git a/core/java/android/midi/MidiManager.java b/core/java/android/midi/MidiManager.java new file mode 100644 index 0000000..f4d1918 --- /dev/null +++ b/core/java/android/midi/MidiManager.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 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.midi; + +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; +import java.util.HashMap; + +/** + * This class is the public application interface to the MIDI service. + * + * <p>You can obtain an instance of this class by calling + * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. + * + * {@samplecode + * MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);} + * @hide + */ +public class MidiManager { + private static final String TAG = "MidiManager"; + + private final Context mContext; + private final IMidiManager mService; + private final IBinder mToken = new Binder(); + + private HashMap<DeviceCallback,DeviceListener> mDeviceListeners = + new HashMap<DeviceCallback,DeviceListener>(); + + // Binder stub for receiving device notifications from MidiService + private class DeviceListener extends IMidiListener.Stub { + private DeviceCallback mCallback; + + public DeviceListener(DeviceCallback callback) { + mCallback = callback; + } + + public void onDeviceAdded(MidiDeviceInfo device) { + mCallback.onDeviceAdded(device); + } + + public void onDeviceRemoved(MidiDeviceInfo device) { + mCallback.onDeviceRemoved(device); + } + } + + // Callback interface clients to receive Device added and removed notifications + public interface DeviceCallback { + void onDeviceAdded(MidiDeviceInfo device); + void onDeviceRemoved(MidiDeviceInfo device); + } + + /** + * @hide + */ + public MidiManager(Context context, IMidiManager service) { + mContext = context; + mService = service; + } + + // Used by clients to register for Device added and removed notifications + public void registerDeviceCallback(DeviceCallback callback) { + DeviceListener deviceListener = new DeviceListener(callback); + try { + mService.registerListener(mToken, deviceListener); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in registerDeviceListener"); + return; + } + mDeviceListeners.put(callback, deviceListener); + } + + // Used by clients to unregister for device added and removed notifications + public void unregisterDeviceCallback(DeviceCallback callback) { + DeviceListener deviceListener = mDeviceListeners.remove(callback); + if (deviceListener != null) { + try { + mService.unregisterListener(mToken, deviceListener); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in unregisterDeviceListener"); + } + } + } + + public MidiDeviceInfo[] getDeviceList() { + try { + return mService.getDeviceList(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getDeviceList"); + return new MidiDeviceInfo[0]; + } + } + + // Use this if you want to communicate with a MIDI device. + public MidiDevice openDevice(MidiDeviceInfo deviceInfo) { + try { + ParcelFileDescriptor pfd = mService.openDevice(mToken, deviceInfo); + if (pfd == null) { + Log.e(TAG, "could not open device " + deviceInfo); + return null; + } + MidiDevice device = new MidiDevice(deviceInfo, pfd); + if (device.open()) { + Log.d(TAG, "openDevice returning " + device); + return device; + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in openDevice"); + } + return null; + } + + // Use this if you want to register and implement a virtual device. + // The MidiDevice returned by this method is the proxy you use to implement the device. + public MidiDevice createVirtualDevice(int numInputPorts, int numOutputPorts, + Bundle properties) { + try { + MidiDevice device = mService.registerVirtualDevice(mToken, + numInputPorts, numOutputPorts, properties); + if (device != null && !device.open()) { + device = null; + } + return device; + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in createVirtualDevice"); + return null; + } + } + + public void closeVirtualDevice(MidiDevice device) { + try { + device.close(); + mService.unregisterVirtualDevice(mToken, device.getInfo()); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in unregisterVirtualDevice"); + } + } +} diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java new file mode 100644 index 0000000..69a33cb --- /dev/null +++ b/core/java/android/midi/MidiOutputPort.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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.midi; + +import java.io.FileInputStream; + +/** + * This class is used for receiving data to a port on a MIDI device + * + * @hide + */ +public final class MidiOutputPort extends MidiPort implements MidiSender { + + private final MidiDevice mDevice; + + /* package */ MidiOutputPort(MidiDevice device, int portNumber) { + super(portNumber); + mDevice = device; + } + + public void connect(MidiReceiver receiver) { + mDevice.connect(receiver, mPortNumber); + } + + public void disconnect(MidiReceiver receiver) { + mDevice.disconnect(receiver, mPortNumber); + } +} diff --git a/core/java/android/midi/MidiPort.java b/core/java/android/midi/MidiPort.java new file mode 100644 index 0000000..e94f62d --- /dev/null +++ b/core/java/android/midi/MidiPort.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 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.midi; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * This class represents a MIDI input or output port + * + * @hide + */ +public class MidiPort { + + protected final int mPortNumber; + + /* package */ MidiPort(int portNumber) { + mPortNumber = portNumber; + } + + /** + * Returns the port number of this port + * + * @return the port's port number + */ + public int getPortNumber() { + return mPortNumber; + } +} diff --git a/core/java/android/midi/MidiReceiver.java b/core/java/android/midi/MidiReceiver.java new file mode 100644 index 0000000..1101105 --- /dev/null +++ b/core/java/android/midi/MidiReceiver.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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.midi; + +import java.io.IOException; + +/** + * Interface for receiving events from a MIDI device. + * + * @hide + */ +public interface MidiReceiver { + // NOTE: the msg array is only valid within the context of this call. + // the byte array may get reused by the MIDI device for the next message. + public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException; +} diff --git a/core/java/android/midi/MidiSender.java b/core/java/android/midi/MidiSender.java new file mode 100644 index 0000000..cba7079 --- /dev/null +++ b/core/java/android/midi/MidiSender.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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.midi; + +/** + * Interface provided by a device to allow attaching + * MidiReceivers to a MIDI device. + * + * @hide + */ +public interface MidiSender { + public void connect(MidiReceiver receiver); + public void disconnect(MidiReceiver receiver); +} diff --git a/core/java/android/midi/MidiUtils.java b/core/java/android/midi/MidiUtils.java new file mode 100644 index 0000000..f80e83a --- /dev/null +++ b/core/java/android/midi/MidiUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 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.midi; + +import android.util.Log; + +/** + * Class containing miscellaneous MIDI utilities. + * + * @hide + */ +public final class MidiUtils { + private static final String TAG = "MidiUtils"; + + /** + * Returns data size of a MIDI message based on the message's command byte + * @param b the message command byte + * @return the message's data length + */ + public static int getMessageDataSize(byte b) { + switch (b & 0xF0) { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + return 2; + case 0xC0: + case 0xD0: + return 1; + case 0xF0: + switch (b & 0x0F) { + case 0x00: + Log.e(TAG, "System Exclusive not supported yet"); + return -1; + case 0x01: + case 0x03: + return 1; + case 0x02: + return 2; + default: + return 0; + } + default: + Log.e(TAG, "unknown MIDI command " + b); + return -1; + } + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 17ee494..865e4a5 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -504,6 +504,8 @@ public class ConnectivityManager { return "MOBILE_EMERGENCY"; case TYPE_PROXY: return "PROXY"; + case TYPE_VPN: + return "VPN"; default: return Integer.toString(type); } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index b8178b4..e5f0153 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1286,7 +1286,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * + icount.globalMethodInvocations()); * } * </pre> + * + * @deprecated Instruction counting is no longer supported. */ + @Deprecated public static class InstructionCount { private static final int NUM_INSTR = OpcodeInfo.MAXIMUM_PACKED_VALUE + 1; diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index c6b2151..4e8ec89 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -395,10 +395,17 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * connected to each other. The two sockets are indistinguishable. */ public static ParcelFileDescriptor[] createSocketPair() throws IOException { + return createSocketPair(SOCK_STREAM); + } + + /** + * @hide + */ + public static ParcelFileDescriptor[] createSocketPair(int type) throws IOException { try { final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); + Os.socketpair(AF_UNIX, type, 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0), new ParcelFileDescriptor(fd1) }; @@ -417,11 +424,18 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * This can also be used to detect remote crashes. */ public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException { + return createReliableSocketPair(SOCK_STREAM); + } + + /** + * @hide + */ + public static ParcelFileDescriptor[] createReliableSocketPair(int type) throws IOException { try { final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); + Os.socketpair(AF_UNIX, type, 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0, comm[0]), new ParcelFileDescriptor(fd1, comm[1]) }; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 555f64c..df2196e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -308,7 +308,7 @@ public final class Settings { /** * Activity Action: Show settings to allow configuration of - * cast endpoints. + * {@link android.media.routing.MediaRouteService media route providers}. * <p> * In some cases, a matching Activity may not exist, so ensure you * safeguard against this. diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl index 899515f..d785c3f 100644 --- a/core/java/android/speech/tts/ITextToSpeechCallback.aidl +++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl @@ -40,7 +40,7 @@ oneway interface ITextToSpeechCallback { * * @param utteranceId Unique id identifying synthesis request. */ - void onStop(String utteranceId); + void onStop(String utteranceId, boolean isStarted); /** * Tells the client that the synthesis has failed. diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index c59ca8a..06e9ce0 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -2066,10 +2066,10 @@ public class TextToSpeech { private boolean mEstablished; private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { - public void onStop(String utteranceId) throws RemoteException { + public void onStop(String utteranceId, boolean isStarted) throws RemoteException { UtteranceProgressListener listener = mUtteranceProgressListener; if (listener != null) { - listener.onDone(utteranceId); + listener.onStop(utteranceId, isStarted); } }; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 9bb7f02..02c9a36 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -455,10 +455,37 @@ public abstract class TextToSpeechService extends Service { private class SynthHandler extends Handler { private SpeechItem mCurrentSpeechItem = null; + private ArrayList<Object> mFlushedObjects = new ArrayList<Object>(); + private boolean mFlushAll; + public SynthHandler(Looper looper) { super(looper); } + private void startFlushingSpeechItems(Object callerIdentity) { + synchronized (mFlushedObjects) { + if (callerIdentity == null) { + mFlushAll = true; + } else { + mFlushedObjects.add(callerIdentity); + } + } + } + private void endFlushingSpeechItems(Object callerIdentity) { + synchronized (mFlushedObjects) { + if (callerIdentity == null) { + mFlushAll = false; + } else { + mFlushedObjects.remove(callerIdentity); + } + } + } + private boolean isFlushed(SpeechItem speechItem) { + synchronized (mFlushedObjects) { + return mFlushAll || mFlushedObjects.contains(speechItem.getCallerIdentity()); + } + } + private synchronized SpeechItem getCurrentSpeechItem() { return mCurrentSpeechItem; } @@ -522,9 +549,13 @@ public abstract class TextToSpeechService extends Service { Runnable runnable = new Runnable() { @Override public void run() { - setCurrentSpeechItem(speechItem); - speechItem.play(); - setCurrentSpeechItem(null); + if (isFlushed(speechItem)) { + speechItem.stop(); + } else { + setCurrentSpeechItem(speechItem); + speechItem.play(); + setCurrentSpeechItem(null); + } } }; Message msg = Message.obtain(this, runnable); @@ -552,12 +583,14 @@ public abstract class TextToSpeechService extends Service { * * Called on a service binder thread. */ - public int stopForApp(Object callerIdentity) { + public int stopForApp(final Object callerIdentity) { if (callerIdentity == null) { return TextToSpeech.ERROR; } - removeCallbacksAndMessages(callerIdentity); + // Flush pending messages from callerIdentity + startFlushingSpeechItems(callerIdentity); + // This stops writing data to the file / or publishing // items to the audio playback handler. // @@ -573,20 +606,39 @@ public abstract class TextToSpeechService extends Service { // Remove any enqueued audio too. mAudioPlaybackHandler.stopForApp(callerIdentity); + // Stop flushing pending messages + Runnable runnable = new Runnable() { + @Override + public void run() { + endFlushingSpeechItems(callerIdentity); + } + }; + sendMessage(Message.obtain(this, runnable)); return TextToSpeech.SUCCESS; } public int stopAll() { + // Order to flush pending messages + startFlushingSpeechItems(null); + // Stop the current speech item unconditionally . SpeechItem current = setCurrentSpeechItem(null); if (current != null) { current.stop(); } - // Remove all other items from the queue. - removeCallbacksAndMessages(null); // Remove all pending playback as well. mAudioPlaybackHandler.stop(); + // Message to stop flushing pending messages + Runnable runnable = new Runnable() { + @Override + public void run() { + endFlushingSpeechItems(null); + } + }; + sendMessage(Message.obtain(this, runnable)); + + return TextToSpeech.SUCCESS; } } @@ -698,7 +750,6 @@ public abstract class TextToSpeechService extends Service { return mCallerIdentity; } - public int getCallerUid() { return mCallerUid; } @@ -752,6 +803,10 @@ public abstract class TextToSpeechService extends Service { protected synchronized boolean isStopped() { return mStopped; } + + protected synchronized boolean isStarted() { + return mStarted; + } } /** @@ -777,7 +832,7 @@ public abstract class TextToSpeechService extends Service { public void dispatchOnStop() { final String utteranceId = getUtteranceId(); if (utteranceId != null) { - mCallbacks.dispatchOnStop(getCallerIdentity(), utteranceId); + mCallbacks.dispatchOnStop(getCallerIdentity(), utteranceId, isStarted()); } } @@ -940,6 +995,8 @@ public abstract class TextToSpeechService extends Service { // turn implies that synthesis would not have started. synthesisCallback.stop(); TextToSpeechService.this.onStop(); + } else { + dispatchOnStop(); } } @@ -1345,11 +1402,11 @@ public abstract class TextToSpeechService extends Service { } } - public void dispatchOnStop(Object callerIdentity, String utteranceId) { + public void dispatchOnStop(Object callerIdentity, String utteranceId, boolean started) { ITextToSpeechCallback cb = getCallbackFor(callerIdentity); if (cb == null) return; try { - cb.onStop(utteranceId); + cb.onStop(utteranceId, started); } catch (RemoteException e) { Log.e(TAG, "Callback onStop failed: " + e); } diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java index 6769794..9eb22ef 100644 --- a/core/java/android/speech/tts/UtteranceProgressListener.java +++ b/core/java/android/speech/tts/UtteranceProgressListener.java @@ -60,6 +60,20 @@ public abstract class UtteranceProgressListener { } /** + * Called when an utterance has been stopped while in progress or flushed from the + * synthesis queue. This can happen if client calls {@link TextToSpeech#stop()} + * or use {@link TextToSpeech#QUEUE_FLUSH} as an argument in + * {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} methods. + * + * @param utteranceId the utterance ID of the utterance. + * @param isStarted If true, then utterance was interrupted while being synthesized + * and it's output is incomplete. If it's false, then utterance was flushed + * before the synthesis started. + */ + public void onStop(String utteranceId, boolean isStarted) { + } + + /** * Wraps an old deprecated OnUtteranceCompletedListener with a shiny new * progress listener. * @@ -83,6 +97,11 @@ public abstract class UtteranceProgressListener { // Left unimplemented, has no equivalent in the old // API. } + + @Override + public void onStop(String utteranceId, boolean isStarted) { + listener.onUtteranceCompleted(utteranceId); + } }; } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 74b7b69..9fdf88d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -28,6 +28,8 @@ import android.util.Log; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import java.util.Arrays; + /** * StaticLayout is a Layout for text that will not be edited after it * is laid out. Use {@link DynamicLayout} for text that may change. @@ -161,7 +163,12 @@ public class StaticLayout extends Layout { float spacingadd, boolean includepad, boolean trackpad, float ellipsizedWidth, TextUtils.TruncateAt ellipsize) { - int[] breakOpp = null; + LineBreaks lineBreaks = new LineBreaks(); + // store span end locations + int[] spanEndCache = new int[4]; + // store fontMetrics per span range + // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range) + int[] fmCache = new int[4 * 4]; final String localeLanguageTag = paint.getTextLocale().toLanguageTag(); mLineCount = 0; @@ -186,7 +193,7 @@ public class StaticLayout extends Layout { else paraEnd++; - int firstWidthLineLimit = mLineCount + 1; + int firstWidthLineCount = 1; int firstWidth = outerWidth; int restWidth = outerWidth; @@ -204,9 +211,8 @@ public class StaticLayout extends Layout { // leading margin spans, not just this particular one if (lms instanceof LeadingMarginSpan2) { LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; - int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2)); - firstWidthLineLimit = Math.max(firstWidthLineLimit, - lmsFirstLine + lms2.getLeadingMarginLineCount()); + firstWidthLineCount = Math.max(firstWidthLineCount, + lms2.getLeadingMarginLineCount()); } } @@ -242,34 +248,23 @@ public class StaticLayout extends Layout { int dir = measured.mDir; boolean easy = measured.mEasy; - breakOpp = nLineBreakOpportunities(localeLanguageTag, chs, paraEnd - paraStart, breakOpp); - int breakOppIndex = 0; - - int width = firstWidth; - - float w = 0; - // here is the offset of the starting character of the line we are currently measuring - int here = paraStart; - - // ok is a character offset located after a word separator (space, tab, number...) where - // we would prefer to cut the current line. Equals to here when no such break was found. - int ok = paraStart; - float okWidth = w; - int okAscent = 0, okDescent = 0, okTop = 0, okBottom = 0; - - // fit is a character offset such that the [here, fit[ range fits in the allowed width. - // We will cut the line there if no ok position is found. - int fit = paraStart; - float fitWidth = w; - int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0; - // same as fitWidth but not including any trailing whitespace - float fitWidthGraphing = w; - - boolean hasTabOrEmoji = false; - boolean hasTab = false; - TabStops tabStops = null; - + // measurement has to be done before performing line breaking + // but we don't want to recompute fontmetrics or span ranges the + // second time, so we cache those and then use those stored values + int fmCacheCount = 0; + int spanEndCacheCount = 0; for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { + if (fmCacheCount * 4 >= fmCache.length) { + int[] grow = new int[fmCacheCount * 4 * 2]; + System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4); + fmCache = grow; + } + + if (spanEndCacheCount >= spanEndCache.length) { + int[] grow = new int[spanEndCacheCount * 2]; + System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount); + spanEndCache = grow; + } if (spanned == null) { spanEnd = paraEnd; @@ -285,201 +280,106 @@ public class StaticLayout extends Layout { measured.addStyleRun(paint, spans, spanLen, fm); } - int fmTop = fm.top; - int fmBottom = fm.bottom; - int fmAscent = fm.ascent; - int fmDescent = fm.descent; - - for (int j = spanStart; j < spanEnd; j++) { - char c = chs[j - paraStart]; - - if (c == CHAR_NEW_LINE) { - // intentionally left empty - } else if (c == CHAR_TAB) { - if (hasTab == false) { - hasTab = true; - hasTabOrEmoji = true; - if (spanned != null) { - // First tab this para, check for tabstops - TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, - paraEnd, TabStopSpan.class); - if (spans.length > 0) { - tabStops = new TabStops(TAB_INCREMENT, spans); - } - } - } - if (tabStops != null) { - w = tabStops.nextTab(w); - } else { - w = TabStops.nextDefaultStop(w, TAB_INCREMENT); - } - } else if (c >= CHAR_FIRST_HIGH_SURROGATE && c <= CHAR_LAST_LOW_SURROGATE - && j + 1 < spanEnd) { - int emoji = Character.codePointAt(chs, j - paraStart); - - if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) { - Bitmap bm = EMOJI_FACTORY.getBitmapFromAndroidPua(emoji); - - if (bm != null) { - Paint whichPaint; - - if (spanned == null) { - whichPaint = paint; - } else { - whichPaint = mWorkPaint; - } - - float wid = bm.getWidth() * -whichPaint.ascent() / bm.getHeight(); - - w += wid; - hasTabOrEmoji = true; - j++; - } else { - w += widths[j - paraStart]; - } - } else { - w += widths[j - paraStart]; - } - } else { - w += widths[j - paraStart]; + // the order of storage here (top, bottom, ascent, descent) has to match the code below + // where these values are retrieved + fmCache[fmCacheCount * 4 + 0] = fm.top; + fmCache[fmCacheCount * 4 + 1] = fm.bottom; + fmCache[fmCacheCount * 4 + 2] = fm.ascent; + fmCache[fmCacheCount * 4 + 3] = fm.descent; + fmCacheCount++; + + spanEndCache[spanEndCacheCount] = spanEnd; + spanEndCacheCount++; + } + + // tab stop locations + int[] variableTabStops = null; + if (spanned != null) { + TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, + paraEnd, TabStopSpan.class); + if (spans.length > 0) { + int[] stops = new int[spans.length]; + for (int i = 0; i < spans.length; i++) { + stops[i] = spans[i].getTabStop(); } + Arrays.sort(stops, 0, stops.length); + variableTabStops = stops; + } + } - boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB || c == CHAR_ZWSP; + int breakCount = nComputeLineBreaks(localeLanguageTag, chs, widths, paraEnd - paraStart, firstWidth, + firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks, + lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length); - if (w <= width || isSpaceOrTab) { - fitWidth = w; - if (!isSpaceOrTab) { - fitWidthGraphing = w; - } - fit = j + 1; - - if (fmTop < fitTop) - fitTop = fmTop; - if (fmAscent < fitAscent) - fitAscent = fmAscent; - if (fmDescent > fitDescent) - fitDescent = fmDescent; - if (fmBottom > fitBottom) - fitBottom = fmBottom; - - while (breakOpp[breakOppIndex] != -1 - && breakOpp[breakOppIndex] < j - paraStart + 1) { - breakOppIndex++; - } - boolean isLineBreak = breakOppIndex < breakOpp.length && - breakOpp[breakOppIndex] == j - paraStart + 1; - - if (isLineBreak) { - okWidth = fitWidthGraphing; - ok = j + 1; - - if (fitTop < okTop) - okTop = fitTop; - if (fitAscent < okAscent) - okAscent = fitAscent; - if (fitDescent > okDescent) - okDescent = fitDescent; - if (fitBottom > okBottom) - okBottom = fitBottom; - } - } else { - int endPos; - int above, below, top, bottom; - float currentTextWidth; - - if (ok != here) { - endPos = ok; - above = okAscent; - below = okDescent; - top = okTop; - bottom = okBottom; - currentTextWidth = okWidth; - } else if (fit != here) { - endPos = fit; - above = fitAscent; - below = fitDescent; - top = fitTop; - bottom = fitBottom; - currentTextWidth = fitWidth; - } else { - // must make progress, so take next character - endPos = here + 1; - // but to deal properly with clusters - // take all zero width characters following that - while (endPos < spanEnd && widths[endPos - paraStart] == 0) { - endPos++; - } - above = fmAscent; - below = fmDescent; - top = fmTop; - bottom = fmBottom; - currentTextWidth = widths[here - paraStart]; - } + int[] breaks = lineBreaks.breaks; + float[] lineWidths = lineBreaks.widths; + boolean[] flags = lineBreaks.flags; - int ellipseEnd = endPos; - if (mMaximumVisibleLineCount == 1 && ellipsize == TextUtils.TruncateAt.MIDDLE) { - ellipseEnd = paraEnd; - } - v = out(source, here, ellipseEnd, - above, below, top, bottom, - v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, hasTabOrEmoji, - needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, - chs, widths, paraStart, ellipsize, ellipsizedWidth, - currentTextWidth, paint, true); - - here = endPos; - j = here - 1; // restart j-span loop from here, compensating for the j++ - ok = fit = here; - w = 0; - fitWidthGraphing = w; - fitAscent = fitDescent = fitTop = fitBottom = 0; - okAscent = okDescent = okTop = okBottom = 0; - - if (--firstWidthLineLimit <= 0) { - width = restWidth; - } + // here is the offset of the starting character of the line we are currently measuring + int here = paraStart; - if (here < spanStart) { - // The text was cut before the beginning of the current span range. - // Exit the span loop, and get spanStart to start over from here. - measured.setPos(here); - spanEnd = here; - break; - } + int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; + int fmCacheIndex = 0; + int spanEndCacheIndex = 0; + int breakIndex = 0; + for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { + // retrieve end of span + spanEnd = spanEndCache[spanEndCacheIndex++]; + + // retrieve cached metrics, order matches above + fm.top = fmCache[fmCacheIndex * 4 + 0]; + fm.bottom = fmCache[fmCacheIndex * 4 + 1]; + fm.ascent = fmCache[fmCacheIndex * 4 + 2]; + fm.descent = fmCache[fmCacheIndex * 4 + 3]; + fmCacheIndex++; + + if (fm.top < fmTop) { + fmTop = fm.top; + } + if (fm.ascent < fmAscent) { + fmAscent = fm.ascent; + } + if (fm.descent > fmDescent) { + fmDescent = fm.descent; + } + if (fm.bottom > fmBottom) { + fmBottom = fm.bottom; + } - if (mLineCount >= mMaximumVisibleLineCount) { - return; - } - } + // skip breaks ending before current span range + while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { + breakIndex++; } - } - if (paraEnd != here && mLineCount < mMaximumVisibleLineCount) { - if ((fitTop | fitBottom | fitDescent | fitAscent) == 0) { - paint.getFontMetricsInt(fm); + while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { + int endPos = paraStart + breaks[breakIndex]; + + v = out(source, here, endPos, + fmAscent, fmDescent, fmTop, fmBottom, + v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex], + needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, + chs, widths, paraStart, ellipsize, ellipsizedWidth, + lineWidths[breakIndex], paint, true); + + if (endPos < spanEnd) { + // preserve metrics for current span + fmTop = fm.top; + fmBottom = fm.bottom; + fmAscent = fm.ascent; + fmDescent = fm.descent; + } else { + fmTop = fmBottom = fmAscent = fmDescent = 0; + } - fitTop = fm.top; - fitBottom = fm.bottom; - fitAscent = fm.ascent; - fitDescent = fm.descent; - } + here = endPos; + breakIndex++; - // Log.e("text", "output rest " + here + " to " + end); - - v = out(source, - here, paraEnd, fitAscent, fitDescent, - fitTop, fitBottom, - v, - spacingmult, spacingadd, chooseHt, - chooseHtv, fm, hasTabOrEmoji, - needMultiply, chdirs, dir, easy, bufEnd, - includepad, trackpad, chs, - widths, paraStart, ellipsize, - ellipsizedWidth, w, paint, paraEnd != bufEnd); + if (mLineCount >= mMaximumVisibleLineCount) { + return; + } + } } - paraStart = paraEnd; - if (paraEnd == bufEnd) break; } @@ -488,7 +388,7 @@ public class StaticLayout extends Layout { mLineCount < mMaximumVisibleLineCount) { // Log.e("text", "output last " + bufEnd); - measured.setPara(source, bufStart, bufEnd, textDir); + measured.setPara(source, bufEnd, bufEnd, textDir); paint.getFontMetricsInt(fm); @@ -848,15 +748,20 @@ public class StaticLayout extends Layout { void prepare() { mMeasured = MeasuredText.obtain(); } - + void finish() { mMeasured = MeasuredText.recycle(mMeasured); } - // returns an array with terminal sentinel value -1 to indicate end - // this is so that arrays can be recycled instead of allocating new arrays - // every time - private static native int[] nLineBreakOpportunities(String locale, char[] text, int length, int[] recycle); + // populates LineBreaks and returns the number of breaks found + // + // the arrays inside the LineBreaks objects are passed in as well + // to reduce the number of JNI calls in the common case where the + // arrays do not have to be resized + private static native int nComputeLineBreaks(String locale, char[] text, float[] widths, + int length, float firstWidth, int firstWidthLineCount, float restWidth, + int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, + int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength); private int mLineCount; private int mTopPadding, mBottomPadding; @@ -884,18 +789,23 @@ public class StaticLayout extends Layout { private static final int TAB_INCREMENT = 20; // same as Layout, but that's private private static final char CHAR_NEW_LINE = '\n'; - private static final char CHAR_TAB = '\t'; - private static final char CHAR_SPACE = ' '; - private static final char CHAR_ZWSP = '\u200B'; private static final double EXTRA_ROUNDING = 0.5; - private static final int CHAR_FIRST_HIGH_SURROGATE = 0xD800; - private static final int CHAR_LAST_LOW_SURROGATE = 0xDFFF; - /* * This is reused across calls to generate() */ private MeasuredText mMeasured; private Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); + + // This is used to return three arrays from a single JNI call when + // performing line breaking + /*package*/ static class LineBreaks { + private static final int INITIAL_SIZE = 16; + public int[] breaks = new int[INITIAL_SIZE]; + public float[] widths = new float[INITIAL_SIZE]; + public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji + // breaks, widths, and flags should all have the same length + } + } diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java index 41a2e5d..bed3a60 100644 --- a/core/java/android/util/Spline.java +++ b/core/java/android/util/Spline.java @@ -165,7 +165,7 @@ public abstract class Spline { throw new IllegalArgumentException("The control points must have " + "monotonic Y values."); } - float h = FloatMath.hypot(a, b); + float h = (float) Math.hypot(a, b); if (h > 9f) { float t = 3f / h; m[i] = t * a * d[i]; diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 60a489b..eaea4d4 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -191,17 +191,17 @@ class GLES20Canvas extends HardwareCanvas { private static native void nInsertReorderBarrier(long renderer, boolean enableReorder); @Override - public int onPreDraw(Rect dirty) { + public void onPreDraw(Rect dirty) { if (dirty != null) { - return nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom, + nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom, mOpaque); } else { - return nPrepare(mRenderer, mOpaque); + nPrepare(mRenderer, mOpaque); } } - private static native int nPrepare(long renderer, boolean opaque); - private static native int nPrepareDirty(long renderer, int left, int top, int right, int bottom, + private static native void nPrepare(long renderer, boolean opaque); + private static native void nPrepareDirty(long renderer, int left, int top, int right, int bottom, boolean opaque); @Override @@ -216,11 +216,11 @@ class GLES20Canvas extends HardwareCanvas { /////////////////////////////////////////////////////////////////////////// @Override - public int callDrawGLFunction2(long drawGLFunction) { - return nCallDrawGLFunction(mRenderer, drawGLFunction); + public void callDrawGLFunction2(long drawGLFunction) { + nCallDrawGLFunction(mRenderer, drawGLFunction); } - private static native int nCallDrawGLFunction(long renderer, long drawGLFunction); + private static native void nCallDrawGLFunction(long renderer, long drawGLFunction); /////////////////////////////////////////////////////////////////////////// // Display list @@ -229,12 +229,12 @@ class GLES20Canvas extends HardwareCanvas { protected static native long nFinishRecording(long renderer); @Override - public int drawRenderNode(RenderNode renderNode, Rect dirty, int flags) { - return nDrawRenderNode(mRenderer, renderNode.getNativeDisplayList(), dirty, flags); + public void drawRenderNode(RenderNode renderNode, int flags) { + nDrawRenderNode(mRenderer, renderNode.getNativeDisplayList(), flags); } - private static native int nDrawRenderNode(long renderer, long renderNode, - Rect dirty, int flags); + private static native void nDrawRenderNode(long renderer, long renderNode, + int flags); /////////////////////////////////////////////////////////////////////////// // Hardware layer @@ -447,7 +447,7 @@ class GLES20Canvas extends HardwareCanvas { return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags); } - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); return nSaveLayer(mRenderer, nativePaint, saveFlags); } @@ -457,7 +457,7 @@ class GLES20Canvas extends HardwareCanvas { public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags) { if (left < right && top < bottom) { - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); return nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags); } return save(saveFlags); @@ -517,16 +517,10 @@ class GLES20Canvas extends HardwareCanvas { @Override public void setDrawFilter(DrawFilter filter) { mFilter = filter; - if (filter == null) { - nResetPaintFilter(mRenderer); - } else if (filter instanceof PaintFlagsDrawFilter) { - PaintFlagsDrawFilter flagsFilter = (PaintFlagsDrawFilter) filter; - nSetupPaintFilter(mRenderer, flagsFilter.clearBits, flagsFilter.setBits); - } + nSetDrawFilter(mRenderer, (filter != null) ? filter.mNativeInt : 0); } - private static native void nResetPaintFilter(long renderer); - private static native void nSetupPaintFilter(long renderer, int clearBits, int setBits); + private static native void nSetDrawFilter(long renderer, long nativeFilter); @Override public DrawFilter getDrawFilter() { @@ -541,7 +535,7 @@ class GLES20Canvas extends HardwareCanvas { public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) { nDrawArc(mRenderer, left, top, right, bottom, - startAngle, sweepAngle, useCenter, paint.mNativePaint); + startAngle, sweepAngle, useCenter, paint.getNativeInstance()); } private static native void nDrawArc(long renderer, float left, float top, @@ -557,7 +551,7 @@ class GLES20Canvas extends HardwareCanvas { public void drawPatch(NinePatch patch, Rect dst, Paint paint) { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } @@ -566,7 +560,7 @@ class GLES20Canvas extends HardwareCanvas { public void drawPatch(NinePatch patch, RectF dst, Paint paint) { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } @@ -577,7 +571,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint); } @@ -587,7 +581,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint); } @@ -597,7 +591,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); int left, top, right, bottom; if (src == null) { @@ -618,7 +612,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { throwIfCannotDraw(bitmap); - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); float left, top, right, bottom; if (src == null) { @@ -663,7 +657,7 @@ class GLES20Canvas extends HardwareCanvas { throw new ArrayIndexOutOfBoundsException(); } - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawBitmap(mRenderer, colors, offset, stride, x, y, width, height, hasAlpha, nativePaint); } @@ -696,7 +690,7 @@ class GLES20Canvas extends HardwareCanvas { checkRange(colors.length, colorOffset, count); } - final long nativePaint = paint == null ? 0 : paint.mNativePaint; + final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, nativePaint); } @@ -707,7 +701,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawCircle(float cx, float cy, float radius, Paint paint) { - nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint); + nDrawCircle(mRenderer, cx, cy, radius, paint.getNativeInstance()); } private static native void nDrawCircle(long renderer, float cx, float cy, @@ -765,7 +759,7 @@ class GLES20Canvas extends HardwareCanvas { if ((offset | count) < 0 || offset + count > pts.length) { throw new IllegalArgumentException("The lines array must contain 4 elements per line."); } - nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint); + nDrawLines(mRenderer, pts, offset, count, paint.getNativeInstance()); } private static native void nDrawLines(long renderer, float[] points, @@ -778,7 +772,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawOval(float left, float top, float right, float bottom, Paint paint) { - nDrawOval(mRenderer, left, top, right, bottom, paint.mNativePaint); + nDrawOval(mRenderer, left, top, right, bottom, paint.getNativeInstance()); } private static native void nDrawOval(long renderer, float left, float top, @@ -795,10 +789,10 @@ class GLES20Canvas extends HardwareCanvas { public void drawPath(Path path, Paint paint) { if (path.isSimplePath) { if (path.rects != null) { - nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint); + nDrawRects(mRenderer, path.rects.mNativeRegion, paint.getNativeInstance()); } } else { - nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint); + nDrawPath(mRenderer, path.mNativePath, paint.getNativeInstance()); } } @@ -828,7 +822,7 @@ class GLES20Canvas extends HardwareCanvas { public void drawPoints(float[] pts, int offset, int count, Paint paint) { if (count < 2) return; - nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint); + nDrawPoints(mRenderer, pts, offset, count, paint.getNativeInstance()); } private static native void nDrawPoints(long renderer, float[] points, @@ -839,7 +833,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawRect(float left, float top, float right, float bottom, Paint paint) { if (left == right || top == bottom) return; - nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint); + nDrawRect(mRenderer, left, top, right, bottom, paint.getNativeInstance()); } private static native void nDrawRect(long renderer, float left, float top, @@ -863,7 +857,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) { - nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint); + nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.getNativeInstance()); } private static native void nDrawRoundRect(long renderer, float left, float top, @@ -876,7 +870,7 @@ class GLES20Canvas extends HardwareCanvas { } nDrawText(mRenderer, text, index, count, x, y, - paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); } private static native void nDrawText(long renderer, char[] text, int index, int count, @@ -890,14 +884,14 @@ class GLES20Canvas extends HardwareCanvas { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags, - paint.mNativePaint, paint.mNativeTypeface); + paint.getNativeInstance(), paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawText(this, start, end, x, y, paint); } else { char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); nDrawText(mRenderer, buf, 0, end - start, x, y, - paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } } @@ -909,7 +903,7 @@ class GLES20Canvas extends HardwareCanvas { } nDrawText(mRenderer, text, start, end, x, y, - paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); } private static native void nDrawText(long renderer, String text, int start, int end, @@ -918,7 +912,7 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawText(String text, float x, float y, Paint paint) { nDrawText(mRenderer, text, 0, text.length(), x, y, - paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); } @Override @@ -929,7 +923,7 @@ class GLES20Canvas extends HardwareCanvas { } nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count, @@ -941,7 +935,7 @@ class GLES20Canvas extends HardwareCanvas { if (text.length() == 0) return; nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); + paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, String text, int start, int end, @@ -956,7 +950,7 @@ class GLES20Canvas extends HardwareCanvas { } nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, isRtl, - paint.mNativePaint, paint.mNativeTypeface); + paint.getNativeInstance(), paint.mNativeTypeface); } private static native void nDrawTextRun(long renderer, char[] text, int index, int count, @@ -972,7 +966,7 @@ class GLES20Canvas extends HardwareCanvas { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawTextRun(mRenderer, text.toString(), start, end, contextStart, - contextEnd, x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); + contextEnd, x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawTextRun(this, start, end, contextStart, contextEnd, x, y, isRtl, paint); @@ -982,7 +976,7 @@ class GLES20Canvas extends HardwareCanvas { char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen, - x, y, isRtl, paint.mNativePaint, paint.mNativeTypeface); + x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } } diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index 18accb8..98e3927 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -43,12 +43,10 @@ public abstract class HardwareCanvas extends Canvas { * Invoked before any drawing operation is performed in this canvas. * * @param dirty The dirty rectangle to update, can be null. - * @return {@link RenderNode#STATUS_DREW} if anything was drawn (such as a call to clear - * the canvas). * * @hide */ - public abstract int onPreDraw(Rect dirty); + public abstract void onPreDraw(Rect dirty); /** * Invoked after all drawing operation have been performed. @@ -64,7 +62,7 @@ public abstract class HardwareCanvas extends Canvas { * @param renderNode The RenderNode to replay. */ public void drawRenderNode(RenderNode renderNode) { - drawRenderNode(renderNode, null, RenderNode.FLAG_CLIP_CHILDREN); + drawRenderNode(renderNode, RenderNode.FLAG_CLIP_CHILDREN); } /** @@ -75,12 +73,9 @@ public abstract class HardwareCanvas extends Canvas { * @param flags Optional flags about drawing, see {@link RenderNode} for * the possible flags. * - * @return One of {@link RenderNode#STATUS_DONE} or {@link RenderNode#STATUS_DREW} - * if anything was drawn. - * * @hide */ - public abstract int drawRenderNode(RenderNode renderNode, Rect dirty, int flags); + public abstract void drawRenderNode(RenderNode renderNode, int flags); /** * Draws the specified layer onto this canvas. @@ -101,11 +96,11 @@ public abstract class HardwareCanvas extends Canvas { * * @param drawGLFunction A native function pointer * - * @return {@link RenderNode#STATUS_DONE} - * * @hide */ - public abstract int callDrawGLFunction2(long drawGLFunction); + public void callDrawGLFunction2(long drawGLFunction) { + // Noop - this is done in the display list recorder subclass + } public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy, CanvasProperty<Float> radius, CanvasProperty<Paint> paint); diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index a130bda..65ae8a6 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -52,7 +52,7 @@ final class HardwareLayer { * @see View#setLayerPaint(android.graphics.Paint) */ public void setLayerPaint(Paint paint) { - nSetLayerPaint(mFinalizer.get(), paint.mNativePaint); + nSetLayerPaint(mFinalizer.get(), paint.getNativeInstance()); mRenderer.pushLayerUpdate(this); } diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 47f72a8..09eb486 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -305,7 +305,7 @@ public class RenderNode { } public boolean setLayerPaint(Paint paint) { - return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.mNativePaint : 0); + return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0); } public boolean setClipBounds(@Nullable Rect rect) { diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 33ce517..83b8100 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -322,7 +322,6 @@ public class Surface implements Parcelable { * @return A canvas for drawing into the surface. * * @throws IllegalStateException If the canvas cannot be locked. - * @hide */ public Canvas lockHardwareCanvas() { synchronized (mLock) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2bb1ebc..753a4e7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15219,7 +15219,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } else { mPrivateFlags &= ~PFLAG_DIRTY_MASK; - ((HardwareCanvas) canvas).drawRenderNode(renderNode, null, flags); + ((HardwareCanvas) canvas).drawRenderNode(renderNode, flags); } } } else if (cache != null) { @@ -17347,7 +17347,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * The specified key should be an id declared in the resources of the * application to ensure it is unique (see the <a - * href={@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>). + * href="{@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>). * Keys identified as belonging to * the Android framework or not associated with any package will cause * an {@link IllegalArgumentException} to be thrown. diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 5bef71f..3ff099a 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -203,43 +203,20 @@ public class InputMethodSubtypeArray { } private static byte[] compress(final byte[] data) { - ByteArrayOutputStream resultStream = null; - GZIPOutputStream zipper = null; - try { - resultStream = new ByteArrayOutputStream(); - zipper = new GZIPOutputStream(resultStream); + try (final ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); + final GZIPOutputStream zipper = new GZIPOutputStream(resultStream)) { zipper.write(data); - } catch(IOException e) { + zipper.finish(); + return resultStream.toByteArray(); + } catch(Exception e) { + Slog.e(TAG, "Failed to compress the data.", e); return null; - } finally { - try { - if (zipper != null) { - zipper.close(); - } - } catch (IOException e) { - zipper = null; - Slog.e(TAG, "Failed to close the stream.", e); - // swallowed, not propagated back to the caller - } - try { - if (resultStream != null) { - resultStream.close(); - } - } catch (IOException e) { - resultStream = null; - Slog.e(TAG, "Failed to close the stream.", e); - // swallowed, not propagated back to the caller - } } - return resultStream != null ? resultStream.toByteArray() : null; } private static byte[] decompress(final byte[] data, final int expectedSize) { - ByteArrayInputStream inputStream = null; - GZIPInputStream unzipper = null; - try { - inputStream = new ByteArrayInputStream(data); - unzipper = new GZIPInputStream(inputStream); + try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + final GZIPInputStream unzipper = new GZIPInputStream(inputStream)) { final byte [] result = new byte[expectedSize]; int totalReadBytes = 0; while (totalReadBytes < result.length) { @@ -254,25 +231,9 @@ public class InputMethodSubtypeArray { return null; } return result; - } catch(IOException e) { + } catch(Exception e) { + Slog.e(TAG, "Failed to decompress the data.", e); return null; - } finally { - try { - if (unzipper != null) { - unzipper.close(); - } - } catch (IOException e) { - Slog.e(TAG, "Failed to close the stream.", e); - // swallowed, not propagated back to the caller - } - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (IOException e) { - Slog.e(TAG, "Failed to close the stream.", e); - // swallowed, not propagated back to the caller - } } } } diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java index ad6e9aa..f487a4e 100644 --- a/core/java/android/webkit/WebResourceResponse.java +++ b/core/java/android/webkit/WebResourceResponse.java @@ -17,6 +17,7 @@ package android.webkit; import java.io.InputStream; +import java.io.StringBufferInputStream; import java.util.Map; /** @@ -40,13 +41,14 @@ public class WebResourceResponse { * * @param mimeType the resource response's MIME type, for example text/html * @param encoding the resource response's encoding - * @param data the input stream that provides the resource response's data + * @param data the input stream that provides the resource response's data. Must not be a + * StringBufferInputStream. */ public WebResourceResponse(String mimeType, String encoding, InputStream data) { mMimeType = mimeType; mEncoding = encoding; - mInputStream = data; + setData(data); } /** @@ -62,7 +64,8 @@ public class WebResourceResponse { * and not empty. * @param responseHeaders the resource response's headers represented as a mapping of header * name -> header value. - * @param data the input stream that provides the resource response's data + * @param data the input stream that provides the resource response's data. Must not be a + * StringBufferInputStream. */ public WebResourceResponse(String mimeType, String encoding, int statusCode, String reasonPhrase, Map<String, String> responseHeaders, InputStream data) { @@ -178,9 +181,16 @@ public class WebResourceResponse { * Sets the input stream that provides the resource response's data. Callers * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}. * - * @param data the input stream that provides the resource response's data + * @param data the input stream that provides the resource response's data. Must not be a + * StringBufferInputStream. */ public void setData(InputStream data) { + // If data is (or is a subclass of) StringBufferInputStream + if (data != null && StringBufferInputStream.class.isAssignableFrom(data.getClass())) { + throw new IllegalArgumentException("StringBufferInputStream is deprecated and must " + + "not be passed to a WebResourceResponse"); + } + mInputStream = data; } diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 474ef42..cafe053 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -117,12 +117,8 @@ public final class WebViewFactory { StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()"); try { - try { - sProviderInstance = providerClass.getConstructor(WebViewDelegate.class) - .newInstance(new WebViewDelegate()); - } catch (Exception e) { - sProviderInstance = providerClass.newInstance(); - } + sProviderInstance = providerClass.getConstructor(WebViewDelegate.class) + .newInstance(new WebViewDelegate()); if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance); return sProviderInstance; } catch (Exception e) { diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 936da32..d5166f3 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1402,7 +1402,7 @@ public class Editor { blockDisplayList.setLeftTopRightBottom(left, top, right, bottom); } - ((HardwareCanvas) canvas).drawRenderNode(blockDisplayList, null, + ((HardwareCanvas) canvas).drawRenderNode(blockDisplayList, 0 /* no child clipping, our TextView parent enforces it */); endOfPreviousBlock = blockEndLine; |
