mobile/android/thirdparty/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /*
michael@0 2 * Copyright (C) 2012 Google Inc.
michael@0 3 *
michael@0 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
michael@0 5 * use this file except in compliance with the License. You may obtain a copy of
michael@0 6 * the License at
michael@0 7 *
michael@0 8 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 9 *
michael@0 10 * Unless required by applicable law or agreed to in writing, software
michael@0 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
michael@0 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
michael@0 13 * License for the specific language governing permissions and limitations under
michael@0 14 * the License.
michael@0 15 */
michael@0 16
michael@0 17 package com.googlecode.eyesfree.braille.selfbraille;
michael@0 18
michael@0 19 import android.content.ComponentName;
michael@0 20 import android.content.Context;
michael@0 21 import android.content.Intent;
michael@0 22 import android.content.ServiceConnection;
michael@0 23 import android.content.pm.PackageInfo;
michael@0 24 import android.content.pm.PackageManager;
michael@0 25 import android.content.pm.Signature;
michael@0 26 import android.os.Binder;
michael@0 27 import android.os.Handler;
michael@0 28 import android.os.IBinder;
michael@0 29 import android.os.Message;
michael@0 30 import android.os.RemoteException;
michael@0 31 import android.util.Log;
michael@0 32
michael@0 33 import java.security.MessageDigest;
michael@0 34 import java.security.NoSuchAlgorithmException;
michael@0 35
michael@0 36 /**
michael@0 37 * Client-side interface to the self brailling interface.
michael@0 38 *
michael@0 39 * Threading: Instances of this object should be created and shut down
michael@0 40 * in a thread with a {@link Looper} associated with it. Other methods may
michael@0 41 * be called on any thread.
michael@0 42 */
michael@0 43 public class SelfBrailleClient {
michael@0 44 private static final String LOG_TAG =
michael@0 45 SelfBrailleClient.class.getSimpleName();
michael@0 46 private static final String ACTION_SELF_BRAILLE_SERVICE =
michael@0 47 "com.googlecode.eyesfree.braille.service.ACTION_SELF_BRAILLE_SERVICE";
michael@0 48 private static final String BRAILLE_BACK_PACKAGE =
michael@0 49 "com.googlecode.eyesfree.brailleback";
michael@0 50 private static final Intent mServiceIntent =
michael@0 51 new Intent(ACTION_SELF_BRAILLE_SERVICE)
michael@0 52 .setPackage(BRAILLE_BACK_PACKAGE);
michael@0 53 /**
michael@0 54 * SHA-1 hash value of the Eyes-Free release key certificate, used to sign
michael@0 55 * BrailleBack. It was generated from the keystore with:
michael@0 56 * $ keytool -exportcert -keystore <keystorefile> -alias android.keystore \
michael@0 57 * > cert
michael@0 58 * $ keytool -printcert -file cert
michael@0 59 */
michael@0 60 // The typecasts are to silence a compiler warning about loss of precision
michael@0 61 private static final byte[] EYES_FREE_CERT_SHA1 = new byte[] {
michael@0 62 (byte) 0x9B, (byte) 0x42, (byte) 0x4C, (byte) 0x2D,
michael@0 63 (byte) 0x27, (byte) 0xAD, (byte) 0x51, (byte) 0xA4,
michael@0 64 (byte) 0x2A, (byte) 0x33, (byte) 0x7E, (byte) 0x0B,
michael@0 65 (byte) 0xB6, (byte) 0x99, (byte) 0x1C, (byte) 0x76,
michael@0 66 (byte) 0xEC, (byte) 0xA4, (byte) 0x44, (byte) 0x61
michael@0 67 };
michael@0 68 /**
michael@0 69 * Delay before the first rebind attempt on bind error or service
michael@0 70 * disconnect.
michael@0 71 */
michael@0 72 private static final int REBIND_DELAY_MILLIS = 500;
michael@0 73 private static final int MAX_REBIND_ATTEMPTS = 5;
michael@0 74
michael@0 75 private final Binder mIdentity = new Binder();
michael@0 76 private final Context mContext;
michael@0 77 private final boolean mAllowDebugService;
michael@0 78 private final SelfBrailleHandler mHandler = new SelfBrailleHandler();
michael@0 79 private boolean mShutdown = false;
michael@0 80
michael@0 81 /**
michael@0 82 * Written in handler thread, read in any thread calling methods on the
michael@0 83 * object.
michael@0 84 */
michael@0 85 private volatile Connection mConnection;
michael@0 86 /** Protected by synchronizing on mHandler. */
michael@0 87 private int mNumFailedBinds = 0;
michael@0 88
michael@0 89 /**
michael@0 90 * Constructs an instance of this class. {@code context} is used to bind
michael@0 91 * to the self braille service. The current thread must have a Looper
michael@0 92 * associated with it. If {@code allowDebugService} is true, this instance
michael@0 93 * will connect to a BrailleBack service without requiring it to be signed
michael@0 94 * by the release key used to sign BrailleBack.
michael@0 95 */
michael@0 96 public SelfBrailleClient(Context context, boolean allowDebugService) {
michael@0 97 mContext = context;
michael@0 98 mAllowDebugService = allowDebugService;
michael@0 99 doBindService();
michael@0 100 }
michael@0 101
michael@0 102 /**
michael@0 103 * Shuts this instance down, deallocating any global resources it is using.
michael@0 104 * This method must be called on the same thread that created this object.
michael@0 105 */
michael@0 106 public void shutdown() {
michael@0 107 mShutdown = true;
michael@0 108 doUnbindService();
michael@0 109 }
michael@0 110
michael@0 111 public void write(WriteData writeData) {
michael@0 112 writeData.validate();
michael@0 113 ISelfBrailleService localService = getSelfBrailleService();
michael@0 114 if (localService != null) {
michael@0 115 try {
michael@0 116 localService.write(mIdentity, writeData);
michael@0 117 } catch (RemoteException ex) {
michael@0 118 Log.e(LOG_TAG, "Self braille write failed", ex);
michael@0 119 }
michael@0 120 }
michael@0 121 }
michael@0 122
michael@0 123 private void doBindService() {
michael@0 124 Connection localConnection = new Connection();
michael@0 125 if (!mContext.bindService(mServiceIntent, localConnection,
michael@0 126 Context.BIND_AUTO_CREATE)) {
michael@0 127 Log.e(LOG_TAG, "Failed to bind to service");
michael@0 128 mHandler.scheduleRebind();
michael@0 129 return;
michael@0 130 }
michael@0 131 mConnection = localConnection;
michael@0 132 Log.i(LOG_TAG, "Bound to self braille service");
michael@0 133 }
michael@0 134
michael@0 135 private void doUnbindService() {
michael@0 136 if (mConnection != null) {
michael@0 137 ISelfBrailleService localService = getSelfBrailleService();
michael@0 138 if (localService != null) {
michael@0 139 try {
michael@0 140 localService.disconnect(mIdentity);
michael@0 141 } catch (RemoteException ex) {
michael@0 142 // Nothing to do.
michael@0 143 }
michael@0 144 }
michael@0 145 mContext.unbindService(mConnection);
michael@0 146 mConnection = null;
michael@0 147 }
michael@0 148 }
michael@0 149
michael@0 150 private ISelfBrailleService getSelfBrailleService() {
michael@0 151 Connection localConnection = mConnection;
michael@0 152 if (localConnection != null) {
michael@0 153 return localConnection.mService;
michael@0 154 }
michael@0 155 return null;
michael@0 156 }
michael@0 157
michael@0 158 private boolean verifyPackage() {
michael@0 159 PackageManager pm = mContext.getPackageManager();
michael@0 160 PackageInfo pi;
michael@0 161 try {
michael@0 162 pi = pm.getPackageInfo(BRAILLE_BACK_PACKAGE,
michael@0 163 PackageManager.GET_SIGNATURES);
michael@0 164 } catch (PackageManager.NameNotFoundException ex) {
michael@0 165 Log.w(LOG_TAG, "Can't verify package " + BRAILLE_BACK_PACKAGE,
michael@0 166 ex);
michael@0 167 return false;
michael@0 168 }
michael@0 169 MessageDigest digest;
michael@0 170 try {
michael@0 171 digest = MessageDigest.getInstance("SHA-1");
michael@0 172 } catch (NoSuchAlgorithmException ex) {
michael@0 173 Log.e(LOG_TAG, "SHA-1 not supported", ex);
michael@0 174 return false;
michael@0 175 }
michael@0 176 // Check if any of the certificates match our hash.
michael@0 177 for (Signature signature : pi.signatures) {
michael@0 178 digest.update(signature.toByteArray());
michael@0 179 if (MessageDigest.isEqual(EYES_FREE_CERT_SHA1, digest.digest())) {
michael@0 180 return true;
michael@0 181 }
michael@0 182 digest.reset();
michael@0 183 }
michael@0 184 if (mAllowDebugService) {
michael@0 185 Log.w(LOG_TAG, String.format(
michael@0 186 "*** %s connected to BrailleBack with invalid (debug?) "
michael@0 187 + "signature ***",
michael@0 188 mContext.getPackageName()));
michael@0 189 return true;
michael@0 190 }
michael@0 191 return false;
michael@0 192 }
michael@0 193 private class Connection implements ServiceConnection {
michael@0 194 // Read in application threads, written in main thread.
michael@0 195 private volatile ISelfBrailleService mService;
michael@0 196
michael@0 197 @Override
michael@0 198 public void onServiceConnected(ComponentName className,
michael@0 199 IBinder binder) {
michael@0 200 if (!verifyPackage()) {
michael@0 201 Log.w(LOG_TAG, String.format("Service certificate mismatch "
michael@0 202 + "for %s, dropping connection",
michael@0 203 BRAILLE_BACK_PACKAGE));
michael@0 204 mHandler.unbindService();
michael@0 205 return;
michael@0 206 }
michael@0 207 Log.i(LOG_TAG, "Connected to self braille service");
michael@0 208 mService = ISelfBrailleService.Stub.asInterface(binder);
michael@0 209 synchronized (mHandler) {
michael@0 210 mNumFailedBinds = 0;
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 @Override
michael@0 215 public void onServiceDisconnected(ComponentName className) {
michael@0 216 Log.e(LOG_TAG, "Disconnected from self braille service");
michael@0 217 mService = null;
michael@0 218 // Retry by rebinding.
michael@0 219 mHandler.scheduleRebind();
michael@0 220 }
michael@0 221 }
michael@0 222
michael@0 223 private class SelfBrailleHandler extends Handler {
michael@0 224 private static final int MSG_REBIND_SERVICE = 1;
michael@0 225 private static final int MSG_UNBIND_SERVICE = 2;
michael@0 226
michael@0 227 public void scheduleRebind() {
michael@0 228 synchronized (this) {
michael@0 229 if (mNumFailedBinds < MAX_REBIND_ATTEMPTS) {
michael@0 230 int delay = REBIND_DELAY_MILLIS << mNumFailedBinds;
michael@0 231 sendEmptyMessageDelayed(MSG_REBIND_SERVICE, delay);
michael@0 232 ++mNumFailedBinds;
michael@0 233 }
michael@0 234 }
michael@0 235 }
michael@0 236
michael@0 237 public void unbindService() {
michael@0 238 sendEmptyMessage(MSG_UNBIND_SERVICE);
michael@0 239 }
michael@0 240
michael@0 241 @Override
michael@0 242 public void handleMessage(Message msg) {
michael@0 243 switch (msg.what) {
michael@0 244 case MSG_REBIND_SERVICE:
michael@0 245 handleRebindService();
michael@0 246 break;
michael@0 247 case MSG_UNBIND_SERVICE:
michael@0 248 handleUnbindService();
michael@0 249 break;
michael@0 250 }
michael@0 251 }
michael@0 252
michael@0 253 private void handleRebindService() {
michael@0 254 if (mShutdown) {
michael@0 255 return;
michael@0 256 }
michael@0 257 if (mConnection != null) {
michael@0 258 doUnbindService();
michael@0 259 }
michael@0 260 doBindService();
michael@0 261 }
michael@0 262
michael@0 263 private void handleUnbindService() {
michael@0 264 doUnbindService();
michael@0 265 }
michael@0 266 }
michael@0 267 }

mercurial