summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/thirdparty/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/thirdparty/java/com')
-rw-r--r--mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java147
-rw-r--r--mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java267
-rw-r--r--mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java192
3 files changed, 606 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java
new file mode 100644
index 000000000..3bdd8c450
--- /dev/null
+++ b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java
@@ -0,0 +1,147 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package com.googlecode.eyesfree.braille.selfbraille;
+
+/**
+ * Interface for a client to control braille output for a part of the
+ * accessibility node tree.
+ */
+public interface ISelfBrailleService extends android.os.IInterface {
+ /** Local-side IPC implementation stub class. */
+ public static abstract class Stub extends android.os.Binder implements
+ com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService {
+ private static final java.lang.String DESCRIPTOR = "com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService";
+
+ /** Construct the stub at attach it to the interface. */
+ public Stub() {
+ this.attachInterface(this, DESCRIPTOR);
+ }
+
+ /**
+ * Cast an IBinder object into an
+ * com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService
+ * interface, generating a proxy if needed.
+ */
+ public static com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService asInterface(
+ android.os.IBinder obj) {
+ if ((obj == null)) {
+ return null;
+ }
+ android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+ if (((iin != null) && (iin instanceof com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService))) {
+ return ((com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService) iin);
+ }
+ return new com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService.Stub.Proxy(
+ obj);
+ }
+
+ @Override
+ public android.os.IBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public boolean onTransact(int code, android.os.Parcel data,
+ android.os.Parcel reply, int flags)
+ throws android.os.RemoteException {
+ switch (code) {
+ case INTERFACE_TRANSACTION: {
+ reply.writeString(DESCRIPTOR);
+ return true;
+ }
+ case TRANSACTION_write: {
+ data.enforceInterface(DESCRIPTOR);
+ android.os.IBinder _arg0;
+ _arg0 = data.readStrongBinder();
+ com.googlecode.eyesfree.braille.selfbraille.WriteData _arg1;
+ if ((0 != data.readInt())) {
+ _arg1 = com.googlecode.eyesfree.braille.selfbraille.WriteData.CREATOR
+ .createFromParcel(data);
+ } else {
+ _arg1 = null;
+ }
+ this.write(_arg0, _arg1);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_disconnect: {
+ data.enforceInterface(DESCRIPTOR);
+ android.os.IBinder _arg0;
+ _arg0 = data.readStrongBinder();
+ this.disconnect(_arg0);
+ return true;
+ }
+ }
+ return super.onTransact(code, data, reply, flags);
+ }
+
+ private static class Proxy implements
+ com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService {
+ private android.os.IBinder mRemote;
+
+ Proxy(android.os.IBinder remote) {
+ mRemote = remote;
+ }
+
+ @Override
+ public android.os.IBinder asBinder() {
+ return mRemote;
+ }
+
+ public java.lang.String getInterfaceDescriptor() {
+ return DESCRIPTOR;
+ }
+
+ @Override
+ public void write(
+ android.os.IBinder clientToken,
+ com.googlecode.eyesfree.braille.selfbraille.WriteData writeData)
+ throws android.os.RemoteException {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeStrongBinder(clientToken);
+ if ((writeData != null)) {
+ _data.writeInt(1);
+ writeData.writeToParcel(_data, 0);
+ } else {
+ _data.writeInt(0);
+ }
+ mRemote.transact(Stub.TRANSACTION_write, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void disconnect(android.os.IBinder clientToken)
+ throws android.os.RemoteException {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeStrongBinder(clientToken);
+ mRemote.transact(Stub.TRANSACTION_disconnect, _data, null,
+ android.os.IBinder.FLAG_ONEWAY);
+ } finally {
+ _data.recycle();
+ }
+ }
+ }
+
+ static final int TRANSACTION_write = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
+ static final int TRANSACTION_disconnect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
+ }
+
+ public void write(android.os.IBinder clientToken,
+ com.googlecode.eyesfree.braille.selfbraille.WriteData writeData)
+ throws android.os.RemoteException;
+
+ public void disconnect(android.os.IBinder clientToken)
+ throws android.os.RemoteException;
+}
diff --git a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java
new file mode 100644
index 000000000..e4a363aca
--- /dev/null
+++ b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.googlecode.eyesfree.braille.selfbraille;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Client-side interface to the self brailling interface.
+ *
+ * Threading: Instances of this object should be created and shut down
+ * in a thread with a {@link Looper} associated with it. Other methods may
+ * be called on any thread.
+ */
+public class SelfBrailleClient {
+ private static final String LOG_TAG =
+ SelfBrailleClient.class.getSimpleName();
+ private static final String ACTION_SELF_BRAILLE_SERVICE =
+ "com.googlecode.eyesfree.braille.service.ACTION_SELF_BRAILLE_SERVICE";
+ private static final String BRAILLE_BACK_PACKAGE =
+ "com.googlecode.eyesfree.brailleback";
+ private static final Intent mServiceIntent =
+ new Intent(ACTION_SELF_BRAILLE_SERVICE)
+ .setPackage(BRAILLE_BACK_PACKAGE);
+ /**
+ * SHA-1 hash value of the Eyes-Free release key certificate, used to sign
+ * BrailleBack. It was generated from the keystore with:
+ * $ keytool -exportcert -keystore <keystorefile> -alias android.keystore \
+ * > cert
+ * $ keytool -printcert -file cert
+ */
+ // The typecasts are to silence a compiler warning about loss of precision
+ private static final byte[] EYES_FREE_CERT_SHA1 = new byte[] {
+ (byte) 0x9B, (byte) 0x42, (byte) 0x4C, (byte) 0x2D,
+ (byte) 0x27, (byte) 0xAD, (byte) 0x51, (byte) 0xA4,
+ (byte) 0x2A, (byte) 0x33, (byte) 0x7E, (byte) 0x0B,
+ (byte) 0xB6, (byte) 0x99, (byte) 0x1C, (byte) 0x76,
+ (byte) 0xEC, (byte) 0xA4, (byte) 0x44, (byte) 0x61
+ };
+ /**
+ * Delay before the first rebind attempt on bind error or service
+ * disconnect.
+ */
+ private static final int REBIND_DELAY_MILLIS = 500;
+ private static final int MAX_REBIND_ATTEMPTS = 5;
+
+ private final Binder mIdentity = new Binder();
+ private final Context mContext;
+ private final boolean mAllowDebugService;
+ private final SelfBrailleHandler mHandler = new SelfBrailleHandler();
+ private boolean mShutdown = false;
+
+ /**
+ * Written in handler thread, read in any thread calling methods on the
+ * object.
+ */
+ private volatile Connection mConnection;
+ /** Protected by synchronizing on mHandler. */
+ private int mNumFailedBinds = 0;
+
+ /**
+ * Constructs an instance of this class. {@code context} is used to bind
+ * to the self braille service. The current thread must have a Looper
+ * associated with it. If {@code allowDebugService} is true, this instance
+ * will connect to a BrailleBack service without requiring it to be signed
+ * by the release key used to sign BrailleBack.
+ */
+ public SelfBrailleClient(Context context, boolean allowDebugService) {
+ mContext = context;
+ mAllowDebugService = allowDebugService;
+ doBindService();
+ }
+
+ /**
+ * Shuts this instance down, deallocating any global resources it is using.
+ * This method must be called on the same thread that created this object.
+ */
+ public void shutdown() {
+ mShutdown = true;
+ doUnbindService();
+ }
+
+ public void write(WriteData writeData) {
+ writeData.validate();
+ ISelfBrailleService localService = getSelfBrailleService();
+ if (localService != null) {
+ try {
+ localService.write(mIdentity, writeData);
+ } catch (RemoteException ex) {
+ Log.e(LOG_TAG, "Self braille write failed", ex);
+ }
+ }
+ }
+
+ private void doBindService() {
+ Connection localConnection = new Connection();
+ if (!mContext.bindService(mServiceIntent, localConnection,
+ Context.BIND_AUTO_CREATE)) {
+ Log.e(LOG_TAG, "Failed to bind to service");
+ mHandler.scheduleRebind();
+ return;
+ }
+ mConnection = localConnection;
+ Log.i(LOG_TAG, "Bound to self braille service");
+ }
+
+ private void doUnbindService() {
+ if (mConnection != null) {
+ ISelfBrailleService localService = getSelfBrailleService();
+ if (localService != null) {
+ try {
+ localService.disconnect(mIdentity);
+ } catch (RemoteException ex) {
+ // Nothing to do.
+ }
+ }
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ }
+ }
+
+ private ISelfBrailleService getSelfBrailleService() {
+ Connection localConnection = mConnection;
+ if (localConnection != null) {
+ return localConnection.mService;
+ }
+ return null;
+ }
+
+ private boolean verifyPackage() {
+ PackageManager pm = mContext.getPackageManager();
+ PackageInfo pi;
+ try {
+ pi = pm.getPackageInfo(BRAILLE_BACK_PACKAGE,
+ PackageManager.GET_SIGNATURES);
+ } catch (PackageManager.NameNotFoundException ex) {
+ Log.w(LOG_TAG, "Can't verify package " + BRAILLE_BACK_PACKAGE,
+ ex);
+ return false;
+ }
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException ex) {
+ Log.e(LOG_TAG, "SHA-1 not supported", ex);
+ return false;
+ }
+ // Check if any of the certificates match our hash.
+ for (Signature signature : pi.signatures) {
+ digest.update(signature.toByteArray());
+ if (MessageDigest.isEqual(EYES_FREE_CERT_SHA1, digest.digest())) {
+ return true;
+ }
+ digest.reset();
+ }
+ if (mAllowDebugService) {
+ Log.w(LOG_TAG, String.format(
+ "*** %s connected to BrailleBack with invalid (debug?) "
+ + "signature ***",
+ mContext.getPackageName()));
+ return true;
+ }
+ return false;
+ }
+ private class Connection implements ServiceConnection {
+ // Read in application threads, written in main thread.
+ private volatile ISelfBrailleService mService;
+
+ @Override
+ public void onServiceConnected(ComponentName className,
+ IBinder binder) {
+ if (!verifyPackage()) {
+ Log.w(LOG_TAG, String.format("Service certificate mismatch "
+ + "for %s, dropping connection",
+ BRAILLE_BACK_PACKAGE));
+ mHandler.unbindService();
+ return;
+ }
+ Log.i(LOG_TAG, "Connected to self braille service");
+ mService = ISelfBrailleService.Stub.asInterface(binder);
+ synchronized (mHandler) {
+ mNumFailedBinds = 0;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ Log.e(LOG_TAG, "Disconnected from self braille service");
+ mService = null;
+ // Retry by rebinding.
+ mHandler.scheduleRebind();
+ }
+ }
+
+ private class SelfBrailleHandler extends Handler {
+ private static final int MSG_REBIND_SERVICE = 1;
+ private static final int MSG_UNBIND_SERVICE = 2;
+
+ public void scheduleRebind() {
+ synchronized (this) {
+ if (mNumFailedBinds < MAX_REBIND_ATTEMPTS) {
+ int delay = REBIND_DELAY_MILLIS << mNumFailedBinds;
+ sendEmptyMessageDelayed(MSG_REBIND_SERVICE, delay);
+ ++mNumFailedBinds;
+ }
+ }
+ }
+
+ public void unbindService() {
+ sendEmptyMessage(MSG_UNBIND_SERVICE);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REBIND_SERVICE:
+ handleRebindService();
+ break;
+ case MSG_UNBIND_SERVICE:
+ handleUnbindService();
+ break;
+ }
+ }
+
+ private void handleRebindService() {
+ if (mShutdown) {
+ return;
+ }
+ if (mConnection != null) {
+ doUnbindService();
+ }
+ doBindService();
+ }
+
+ private void handleUnbindService() {
+ doUnbindService();
+ }
+ }
+}
diff --git a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java
new file mode 100644
index 000000000..ef81a2990
--- /dev/null
+++ b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.googlecode.eyesfree.braille.selfbraille;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * Represents what should be shown on the braille display for a
+ * part of the accessibility node tree.
+ */
+public class WriteData implements Parcelable {
+
+ private static final String PROP_SELECTION_START = "selectionStart";
+ private static final String PROP_SELECTION_END = "selectionEnd";
+
+ private AccessibilityNodeInfo mAccessibilityNodeInfo;
+ private CharSequence mText;
+ private Bundle mProperties = Bundle.EMPTY;
+
+ /**
+ * Returns a new {@link WriteData} instance for the given {@code view}.
+ */
+ public static WriteData forView(View view) {
+ AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(view);
+ WriteData writeData = new WriteData();
+ writeData.mAccessibilityNodeInfo = node;
+ return writeData;
+ }
+
+ public static WriteData forInfo(AccessibilityNodeInfo info){
+ WriteData writeData = new WriteData();
+ writeData.mAccessibilityNodeInfo = info;
+ return writeData;
+ }
+
+
+ public AccessibilityNodeInfo getAccessibilityNodeInfo() {
+ return mAccessibilityNodeInfo;
+ }
+
+ /**
+ * Sets the text to be displayed when the accessibility node associated
+ * with this instance has focus. If this method is not called (or
+ * {@code text} is {@code null}), this client relinquishes control over
+ * this node.
+ */
+ public WriteData setText(CharSequence text) {
+ mText = text;
+ return this;
+ }
+
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Sets the start position in the text of a text selection or cursor that
+ * should be marked on the display. A negative value (the default) means
+ * no selection will be added.
+ */
+ public WriteData setSelectionStart(int v) {
+ writableProperties().putInt(PROP_SELECTION_START, v);
+ return this;
+ }
+
+ /**
+ * @see {@link #setSelectionStart}.
+ */
+ public int getSelectionStart() {
+ return mProperties.getInt(PROP_SELECTION_START, -1);
+ }
+
+ /**
+ * Sets the end of the text selection to be marked on the display. This
+ * value should only be non-negative if the selection start is
+ * non-negative. If this value is <= the selection start, the selection
+ * is a cursor. Otherwise, the selection covers the range from
+ * start(inclusive) to end (exclusive).
+ *
+ * @see {@link android.text.Selection}.
+ */
+ public WriteData setSelectionEnd(int v) {
+ writableProperties().putInt(PROP_SELECTION_END, v);
+ return this;
+ }
+
+ /**
+ * @see {@link #setSelectionEnd}.
+ */
+ public int getSelectionEnd() {
+ return mProperties.getInt(PROP_SELECTION_END, -1);
+ }
+
+ private Bundle writableProperties() {
+ if (mProperties == Bundle.EMPTY) {
+ mProperties = new Bundle();
+ }
+ return mProperties;
+ }
+
+ /**
+ * Checks constraints on the fields that must be satisfied before sending
+ * this instance to the self braille service.
+ * @throws IllegalStateException
+ */
+ public void validate() throws IllegalStateException {
+ if (mAccessibilityNodeInfo == null) {
+ throw new IllegalStateException(
+ "Accessibility node info can't be null");
+ }
+ int selectionStart = getSelectionStart();
+ int selectionEnd = getSelectionEnd();
+ if (mText == null) {
+ if (selectionStart > 0 || selectionEnd > 0) {
+ throw new IllegalStateException(
+ "Selection can't be set without text");
+ }
+ } else {
+ if (selectionStart < 0 && selectionEnd >= 0) {
+ throw new IllegalStateException(
+ "Selection end without start");
+ }
+ int textLength = mText.length();
+ if (selectionStart > textLength || selectionEnd > textLength) {
+ throw new IllegalStateException("Selection out of bounds");
+ }
+ }
+ }
+
+ // For Parcelable support.
+
+ public static final Parcelable.Creator<WriteData> CREATOR =
+ new Parcelable.Creator<WriteData>() {
+ @Override
+ public WriteData createFromParcel(Parcel in) {
+ return new WriteData(in);
+ }
+
+ @Override
+ public WriteData[] newArray(int size) {
+ return new WriteData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <strong>Note:</strong> The {@link AccessibilityNodeInfo} will be
+ * recycled by this method, don't try to use this more than once.
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ mAccessibilityNodeInfo.writeToParcel(out, flags);
+ // The above call recycles the node, so make sure we don't use it
+ // anymore.
+ mAccessibilityNodeInfo = null;
+ out.writeString(mText.toString());
+ out.writeBundle(mProperties);
+ }
+
+ private WriteData() {
+ }
+
+ private WriteData(Parcel in) {
+ mAccessibilityNodeInfo =
+ AccessibilityNodeInfo.CREATOR.createFromParcel(in);
+ mText = in.readString();
+ mProperties = in.readBundle();
+ }
+}