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.os.Environment; 23 import android.support.v4.app.ActivityCompat; 24 import android.util.Log; 25 26 import com.example.android.wearable.runtimepermissions.common.Constants; 27 28 import com.google.android.gms.common.api.GoogleApiClient; 29 import com.google.android.gms.common.api.PendingResult; 30 import com.google.android.gms.wearable.DataMap; 31 import com.google.android.gms.wearable.MessageApi; 32 import com.google.android.gms.wearable.MessageEvent; 33 import com.google.android.gms.wearable.Wearable; 34 import com.google.android.gms.wearable.WearableListenerService; 35 36 import java.io.File; 37 import java.util.concurrent.TimeUnit; 38 39 /** 40 * Handles all incoming requests for phone data (and permissions) from wear devices. 41 */ 42 public class IncomingRequestPhoneService extends WearableListenerService { 43 44 private static final String TAG = "IncomingRequestService"; 45 46 @Override 47 public void onCreate() { 48 super.onCreate(); 49 Log.d(TAG, "onCreate()"); 50 } 51 52 @Override 53 public void onMessageReceived(MessageEvent messageEvent) { 54 super.onMessageReceived(messageEvent); 55 Log.d(TAG, "onMessageReceived(): " + messageEvent); 56 57 String messagePath = messageEvent.getPath(); 58 59 if (messagePath.equals(Constants.MESSAGE_PATH_PHONE)) { 60 61 DataMap dataMap = DataMap.fromByteArray(messageEvent.getData()); 62 int requestType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0); 63 64 if (requestType == Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION) { 65 promptUserForStoragePermission(messageEvent.getSourceNodeId()); 66 67 } else if (requestType == Constants.COMM_TYPE_REQUEST_DATA) { 68 respondWithStorageInformation(messageEvent.getSourceNodeId()); 69 } 70 } 71 } 72 73 private void promptUserForStoragePermission(String nodeId) { 74 boolean storagePermissionApproved = 75 ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) 76 == PackageManager.PERMISSION_GRANTED; 77 78 if (storagePermissionApproved) { 79 DataMap dataMap = new DataMap(); 80 dataMap.putInt(Constants.KEY_COMM_TYPE, 81 Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION); 82 sendMessage(nodeId, dataMap); 83 } else { 84 // Launch Phone Activity to grant storage permissions. 85 Intent startIntent = new Intent(this, PhonePermissionRequestActivity.class); 86 startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 87 88 /* This extra is included to alert MainPhoneActivity to send back the permission 89 * results after the user has made their decision in PhonePermissionRequestActivity 90 * and it finishes. 91 */ 92 startIntent.putExtra(MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, true); 93 startActivity(startIntent); 94 } 95 } 96 97 private void respondWithStorageInformation(String nodeId) { 98 99 boolean storagePermissionApproved = 100 ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) 101 == PackageManager.PERMISSION_GRANTED; 102 103 if (!storagePermissionApproved) { 104 DataMap dataMap = new DataMap(); 105 dataMap.putInt(Constants.KEY_COMM_TYPE, 106 Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED); 107 sendMessage(nodeId, dataMap); 108 } else { 109 /* To keep the sample simple, we are only displaying the top level list of directories. 110 * Otherwise, it will return a message that the media wasn't available. 111 */ 112 StringBuilder stringBuilder = new StringBuilder(); 113 114 if (isExternalStorageReadable()) { 115 File externalStorageDirectory = Environment.getExternalStorageDirectory(); 116 String[] fileList = externalStorageDirectory.list(); 117 118 if (fileList.length > 0) { 119 stringBuilder.append("List of directories on phone:\n"); 120 for (String file : fileList) { 121 stringBuilder.append(" - " + file + "\n"); 122 } 123 } else { 124 stringBuilder.append("No files in external storage."); 125 } 126 } else { 127 stringBuilder.append("No external media is available."); 128 } 129 130 // Send valid results 131 DataMap dataMap = new DataMap(); 132 dataMap.putInt(Constants.KEY_COMM_TYPE, 133 Constants.COMM_TYPE_RESPONSE_DATA); 134 dataMap.putString(Constants.KEY_PAYLOAD, stringBuilder.toString()); 135 sendMessage(nodeId, dataMap); 136 137 } 138 } 139 140 private void sendMessage(String nodeId, DataMap dataMap) { 141 Log.d(TAG, "sendMessage() Node: " + nodeId); 142 143 GoogleApiClient client = new GoogleApiClient.Builder(this) 144 .addApi(Wearable.API) 145 .build(); 146 client.blockingConnect(Constants.CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS); 147 148 149 PendingResult<MessageApi.SendMessageResult> pendingMessageResult = 150 Wearable.MessageApi.sendMessage( 151 client, 152 nodeId, 153 Constants.MESSAGE_PATH_WEAR, 154 dataMap.toByteArray()); 155 156 MessageApi.SendMessageResult sendMessageResult = 157 pendingMessageResult.await( 158 Constants.CONNECTION_TIME_OUT_MS, 159 TimeUnit.MILLISECONDS); 160 161 if (!sendMessageResult.getStatus().isSuccess()) { 162 Log.d(TAG, "Sending message failed, status: " 163 + sendMessageResult.getStatus()); 164 } else { 165 Log.d(TAG, "Message sent successfully"); 166 } 167 client.disconnect(); 168 } 169 170 private boolean isExternalStorageReadable() { 171 String state = Environment.getExternalStorageState(); 172 173 return Environment.MEDIA_MOUNTED.equals(state) 174 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state); 175 } 176 }