1 /* 2 * Copyright (C) 2019 The Android Open Source Project 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 android.inputmethodservice.cts.devicetest; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.inputmethodservice.cts.ime.CtsBaseInputMethod; 22 import android.os.Bundle; 23 import android.os.Process; 24 import android.os.SystemClock; 25 import android.os.UserHandle; 26 import android.text.TextUtils; 27 import android.view.inputmethod.EditorInfo; 28 import android.view.inputmethod.InputConnection; 29 import android.view.inputmethod.InputConnectionWrapper; 30 import android.widget.EditText; 31 import android.widget.LinearLayout; 32 33 import androidx.test.runner.AndroidJUnit4; 34 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.util.concurrent.BlockingDeque; 39 import java.util.concurrent.LinkedBlockingDeque; 40 import java.util.concurrent.TimeUnit; 41 42 /** 43 * Provides test scenarios for multi-user scenario. 44 */ 45 @RunWith(AndroidJUnit4.class) 46 public class MultiUserDeviceTest { 47 48 private static final long TIMEOUT_MILLISEC = TimeUnit.SECONDS.toMillis(10); 49 50 /** 51 * A special {@link InputConnection} to receive {@link UserHandle} from 52 * {@link CtsBaseInputMethod} via {@link InputConnection#performPrivateCommand(String, Bundle)}. 53 */ 54 private static final class ReplyReceivingInputConnection extends InputConnectionWrapper { 55 private final String mSessionKey; 56 private final BlockingDeque<UserHandle> mResultQueue; 57 ReplyReceivingInputConnection(InputConnection target, String sessionKey, BlockingDeque<UserHandle> resultQueue)58 ReplyReceivingInputConnection(InputConnection target, String sessionKey, 59 BlockingDeque<UserHandle> resultQueue) { 60 super(target, false); 61 mSessionKey = sessionKey; 62 mResultQueue = resultQueue; 63 } 64 65 /** 66 * {@inheritDoc} 67 */ 68 @Override performPrivateCommand(String action, Bundle data)69 public boolean performPrivateCommand(String action, Bundle data) { 70 if (TextUtils.equals(CtsBaseInputMethod.ACTION_KEY_REPLY_USER_HANDLE, action) 71 && TextUtils.equals(mSessionKey, data.getString( 72 CtsBaseInputMethod.BUNDLE_KEY_REPLY_USER_HANDLE_SESSION_ID))) { 73 final UserHandle userHandle = 74 data.getParcelable(CtsBaseInputMethod.BUNDLE_KEY_REPLY_USER_HANDLE); 75 if (userHandle != null) { 76 mResultQueue.add(userHandle); 77 } 78 return true; 79 } 80 return super.performPrivateCommand(action, data); 81 } 82 } 83 84 /** Test to check if the app is connecting to the IME that runs under the same user ID. */ 85 @Test testConnectingToTheSameUserIme()86 public void testConnectingToTheSameUserIme() throws Exception { 87 final String sessionId = Long.toString(SystemClock.elapsedRealtimeNanos()); 88 final BlockingDeque<UserHandle> resultQueue = new LinkedBlockingDeque<>(); 89 90 TestActivity.startSync(activity -> { 91 final LinearLayout layout = new LinearLayout(activity); 92 layout.setOrientation(LinearLayout.VERTICAL); 93 final EditText editText = new EditText(activity) { 94 @Override 95 public InputConnection onCreateInputConnection(EditorInfo editorInfo) { 96 final InputConnection original = super.onCreateInputConnection(editorInfo); 97 if (editorInfo.extras == null) { 98 editorInfo.extras = new Bundle(); 99 } 100 editorInfo.extras.putString( 101 CtsBaseInputMethod.EDITOR_INFO_KEY_REPLY_USER_HANDLE_SESSION_ID, 102 sessionId); 103 return new ReplyReceivingInputConnection(original, sessionId, resultQueue); 104 } 105 }; 106 editText.requestFocus(); 107 layout.addView(editText); 108 return layout; 109 }); 110 111 final UserHandle result = resultQueue.poll(TIMEOUT_MILLISEC, TimeUnit.MILLISECONDS); 112 assertEquals(Process.myUserHandle(), result); 113 } 114 } 115