1 /*
2  * Copyright (C) 2015 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.wearable.runtimepermissions;
18 
19 import android.Manifest;
20 import android.content.Intent;
21 import android.content.pm.PackageManager;
22 import android.hardware.Sensor;
23 import android.hardware.SensorManager;
24 import android.support.v4.app.ActivityCompat;
25 import android.util.Log;
26 
27 import com.example.android.wearable.runtimepermissions.common.Constants;
28 
29 import com.google.android.gms.common.ConnectionResult;
30 import com.google.android.gms.common.api.GoogleApiClient;
31 import com.google.android.gms.common.api.PendingResult;
32 import com.google.android.gms.wearable.CapabilityApi;
33 import com.google.android.gms.wearable.CapabilityInfo;
34 import com.google.android.gms.wearable.DataMap;
35 import com.google.android.gms.wearable.MessageApi;
36 import com.google.android.gms.wearable.MessageEvent;
37 import com.google.android.gms.wearable.Node;
38 import com.google.android.gms.wearable.Wearable;
39 import com.google.android.gms.wearable.WearableListenerService;
40 
41 import java.util.List;
42 import java.util.Set;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * Handles all incoming requests for wear data (and permissions) from phone devices.
47  */
48 public class IncomingRequestWearService extends WearableListenerService {
49 
50     private static final String TAG = "IncomingRequestService";
51 
IncomingRequestWearService()52     public IncomingRequestWearService() {
53         Log.d(TAG, "IncomingRequestWearService()");
54     }
55 
56     @Override
onCreate()57     public void onCreate() {
58         super.onCreate();
59         Log.d(TAG, "onCreate()");
60     }
61 
62     @Override
onMessageReceived(MessageEvent messageEvent)63     public void onMessageReceived(MessageEvent messageEvent) {
64         Log.d(TAG, "onMessageReceived(): " + messageEvent);
65 
66         String messagePath = messageEvent.getPath();
67 
68         if (messagePath.equals(Constants.MESSAGE_PATH_WEAR)) {
69             DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
70 
71             int requestType = dataMap.getInt(Constants.KEY_COMM_TYPE);
72 
73             if (requestType == Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION) {
74                 promptUserForSensorPermission();
75 
76             } else if (requestType == Constants.COMM_TYPE_REQUEST_DATA) {
77                 respondWithSensorInformation();
78             }
79         }
80     }
81 
promptUserForSensorPermission()82     private void promptUserForSensorPermission() {
83         Log.d(TAG, "promptUserForSensorPermission()");
84 
85         boolean sensorPermissionApproved =
86                 ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
87                         == PackageManager.PERMISSION_GRANTED;
88 
89         if (sensorPermissionApproved) {
90             DataMap dataMap = new DataMap();
91             dataMap.putInt(Constants.KEY_COMM_TYPE,
92                     Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
93             sendMessage(dataMap);
94         } else {
95             // Launch Activity to grant sensor permissions.
96             Intent startIntent = new Intent(this, MainWearActivity.class);
97             startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
98             startIntent.putExtra(MainWearActivity.EXTRA_PROMPT_PERMISSION_FROM_PHONE, true);
99             startActivity(startIntent);
100         }
101     }
102 
respondWithSensorInformation()103     private void respondWithSensorInformation() {
104         Log.d(TAG, "respondWithSensorInformation()");
105 
106         boolean sensorPermissionApproved =
107                 ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
108                         == PackageManager.PERMISSION_GRANTED;
109 
110         if (!sensorPermissionApproved) {
111             DataMap dataMap = new DataMap();
112             dataMap.putInt(Constants.KEY_COMM_TYPE,
113                     Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED);
114             sendMessage(dataMap);
115         } else {
116             /* To keep the sample simple, we are only displaying the number of sensors. You could do
117              * something much more complicated.
118              */
119             SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
120             List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
121             int numberOfSensorsOnDevice = sensorList.size();
122 
123             String sensorSummary = numberOfSensorsOnDevice + " sensors on wear device(s)!";
124             DataMap dataMap = new DataMap();
125             dataMap.putInt(Constants.KEY_COMM_TYPE,
126                     Constants.COMM_TYPE_RESPONSE_DATA);
127             dataMap.putString(Constants.KEY_PAYLOAD, sensorSummary);
128             sendMessage(dataMap);
129         }
130     }
131 
sendMessage(DataMap dataMap)132     private void sendMessage(DataMap dataMap) {
133 
134         Log.d(TAG, "sendMessage(): " + dataMap);
135 
136         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
137                 .addApi(Wearable.API)
138                 .build();
139         ConnectionResult connectionResult =
140                 googleApiClient.blockingConnect(
141                         Constants.CONNECTION_TIME_OUT_MS,
142                         TimeUnit.MILLISECONDS);
143 
144         if (!connectionResult.isSuccess()) {
145             Log.d(TAG, "Google API Client failed to connect.");
146             return;
147         }
148 
149         PendingResult<CapabilityApi.GetCapabilityResult> pendingCapabilityResult =
150                 Wearable.CapabilityApi.getCapability(
151                         googleApiClient,
152                         Constants.CAPABILITY_PHONE_APP,
153                         CapabilityApi.FILTER_REACHABLE);
154 
155         CapabilityApi.GetCapabilityResult getCapabilityResult =
156                 pendingCapabilityResult.await(
157                         Constants.CONNECTION_TIME_OUT_MS,
158                         TimeUnit.MILLISECONDS);
159 
160         if (!getCapabilityResult.getStatus().isSuccess()) {
161             Log.d(TAG, "CapabilityApi failed to return any results.");
162             googleApiClient.disconnect();
163             return;
164         }
165 
166         CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
167         String phoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
168 
169         PendingResult<MessageApi.SendMessageResult> pendingMessageResult =
170                 Wearable.MessageApi.sendMessage(
171                         googleApiClient,
172                         phoneNodeId,
173                         Constants.MESSAGE_PATH_PHONE,
174                         dataMap.toByteArray());
175 
176         MessageApi.SendMessageResult sendMessageResult =
177                 pendingMessageResult.await(Constants.CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
178 
179         if (!sendMessageResult.getStatus().isSuccess()) {
180             Log.d(TAG, "Sending message failed, onResult: " + sendMessageResult.getStatus());
181         } else {
182             Log.d(TAG, "Message sent successfully");
183         }
184 
185         googleApiClient.disconnect();
186     }
187 
188     /*
189      * There should only ever be one phone in a node set (much less w/ the correct capability), so
190      * I am just grabbing the first one (which should be the only one).
191      */
pickBestNodeId(Set<Node> nodes)192     private String pickBestNodeId(Set<Node> nodes) {
193 
194         Log.d(TAG, "pickBestNodeId: " + nodes);
195 
196 
197         String bestNodeId = null;
198         /* Find a nearby node or pick one arbitrarily. There should be only one phone connected
199          * that supports this sample.
200          */
201         for (Node node : nodes) {
202             if (node.isNearby()) {
203                 return node.getId();
204             }
205             bestNodeId = node.getId();
206         }
207         return bestNodeId;
208     }
209 }