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.

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

mercurial