1 /* 2 * Copyright (C) 2022 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 com.example.odpclient; 18 19 import android.adservices.ondevicepersonalization.OnDevicePersonalizationManager; 20 import android.adservices.ondevicepersonalization.OnDevicePersonalizationManager.ExecuteResult; 21 import android.app.Activity; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.pm.PackageInfo; 25 import android.content.pm.PackageManager; 26 import android.content.res.Configuration; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.OutcomeReceiver; 31 import android.os.PersistableBundle; 32 import android.os.Trace; 33 import android.util.Log; 34 import android.view.SurfaceControlViewHost.SurfacePackage; 35 import android.view.SurfaceHolder; 36 import android.view.SurfaceView; 37 import android.view.View; 38 import android.widget.Button; 39 import android.widget.EditText; 40 import android.widget.Toast; 41 42 import com.google.common.util.concurrent.Futures; 43 44 import java.util.concurrent.CountDownLatch; 45 import java.util.concurrent.Executor; 46 import java.util.concurrent.Executors; 47 import java.util.concurrent.atomic.AtomicReference; 48 49 public class MainActivity extends Activity { 50 private static final String TAG = "OdpClient"; 51 private static final String SERVICE_PACKAGE = "com.example.odpsamplenetwork"; 52 private static final String SERVICE_CLASS = "com.example.odpsamplenetwork.SampleService"; 53 private static final String ODP_APEX = "com.google.android.ondevicepersonalization"; 54 private static final String ADSERVICES_APEX = "com.google.android.adservices"; 55 56 private EditText mTextBox; 57 private Button mGetAdButton; 58 private EditText mScheduleTrainingTextBox; 59 private EditText mScheduleIntervalTextBox; 60 private Button mScheduleTrainingButton; 61 private Button mCancelTrainingButton; 62 private EditText mReportConversionTextBox; 63 private Button mReportConversionButton; 64 private SurfaceView mRenderedView; 65 private Context mContext; 66 private static Executor sCallbackExecutor = Executors.newSingleThreadExecutor(); 67 68 class SurfaceCallback implements SurfaceHolder.Callback { surfaceCreated(SurfaceHolder holder)69 @Override public void surfaceCreated(SurfaceHolder holder) { 70 Log.d(TAG, "surfaceCreated"); 71 } surfaceDestroyed(SurfaceHolder holder)72 @Override public void surfaceDestroyed(SurfaceHolder holder) { 73 Log.d(TAG, "surfaceDestroyed"); 74 } surfaceChanged( SurfaceHolder holder, int format, int width, int height)75 @Override public void surfaceChanged( 76 SurfaceHolder holder, int format, int width, int height) { 77 Log.d(TAG, "surfaceChanged"); 78 } 79 } 80 81 @Override onCreate(Bundle savedInstanceState)82 public void onCreate(Bundle savedInstanceState) { 83 Log.d(TAG, "onCreate"); 84 super.onCreate(savedInstanceState); 85 setContentView(R.layout.activity_main); 86 mContext = getApplicationContext(); 87 mRenderedView = findViewById(R.id.rendered_view); 88 mRenderedView.setVisibility(View.INVISIBLE); 89 mRenderedView.getHolder().addCallback(new SurfaceCallback()); 90 mGetAdButton = findViewById(R.id.get_ad_button); 91 mScheduleTrainingButton = findViewById(R.id.schedule_training_button); 92 mCancelTrainingButton = findViewById(R.id.cancel_training_button); 93 mReportConversionButton = findViewById(R.id.report_conversion_button); 94 mTextBox = findViewById(R.id.text_box); 95 mScheduleTrainingTextBox = findViewById(R.id.schedule_training_text_box); 96 mScheduleIntervalTextBox = findViewById(R.id.schedule_interval_text_box); 97 mReportConversionTextBox = findViewById(R.id.report_conversion_text_box); 98 registerGetAdButton(); 99 registerScheduleTrainingButton(); 100 registerReportConversionButton(); 101 registerCancelTrainingButton(); 102 103 Futures.submit( 104 () -> printDebuggingInfo(), 105 sCallbackExecutor); 106 } 107 registerGetAdButton()108 private void registerGetAdButton() { 109 mGetAdButton.setOnClickListener( 110 v -> makeRequest()); 111 } 112 registerReportConversionButton()113 private void registerReportConversionButton() { 114 mReportConversionButton.setOnClickListener(v -> reportConversion()); 115 } 116 getOdpManager()117 private OnDevicePersonalizationManager getOdpManager() { 118 return mContext.getSystemService(OnDevicePersonalizationManager.class); 119 } 120 makeRequest()121 private void makeRequest() { 122 try { 123 var odpManager = getOdpManager(); 124 CountDownLatch latch = new CountDownLatch(1); 125 Log.i(TAG, "Starting execute() " + getResources().getString(R.string.get_ad) 126 + " with " + mTextBox.getHint().toString() + ": " 127 + mTextBox.getText().toString()); 128 AtomicReference<ExecuteResult> slotResultHandle = new AtomicReference<>(); 129 PersistableBundle appParams = new PersistableBundle(); 130 appParams.putString("keyword", mTextBox.getText().toString()); 131 132 Trace.beginAsyncSection("OdpClient:makeRequest:odpManager.execute", 0); 133 odpManager.execute( 134 ComponentName.createRelative( 135 SERVICE_PACKAGE, 136 SERVICE_CLASS), 137 appParams, 138 sCallbackExecutor, 139 new OutcomeReceiver<ExecuteResult, Exception>() { 140 @Override 141 public void onResult(ExecuteResult result) { 142 Trace.endAsyncSection("OdpClient:makeRequest:odpManager.execute", 0); 143 Log.i(TAG, "execute() success: " + result); 144 if (result != null) { 145 slotResultHandle.set(result); 146 } else { 147 Log.e(TAG, "No results!"); 148 } 149 latch.countDown(); 150 } 151 152 @Override 153 public void onError(Exception e) { 154 Trace.endAsyncSection("OdpClient:makeRequest:odpManager.execute", 0); 155 makeToast("execute() error: " + e.toString()); 156 latch.countDown(); 157 } 158 }); 159 latch.await(); 160 Log.d(TAG, "makeRequest:odpManager.execute wait success"); 161 162 Trace.beginAsyncSection("OdpClient:makeRequest:odpManager.requestSurfacePackage", 0); 163 odpManager.requestSurfacePackage( 164 slotResultHandle.get().getSurfacePackageToken(), 165 mRenderedView.getHostToken(), 166 getDisplay().getDisplayId(), 167 mRenderedView.getWidth(), 168 mRenderedView.getHeight(), 169 sCallbackExecutor, 170 new OutcomeReceiver<SurfacePackage, Exception>() { 171 @Override 172 public void onResult(SurfacePackage surfacePackage) { 173 Trace.endAsyncSection( 174 "OdpClient:makeRequest:odpManager.requestSurfacePackage", 0); 175 Log.i(TAG, 176 "requestSurfacePackage() success: " 177 + surfacePackage.toString()); 178 new Handler(Looper.getMainLooper()).post(() -> { 179 if (surfacePackage != null) { 180 mRenderedView.setChildSurfacePackage( 181 surfacePackage); 182 } 183 mRenderedView.setZOrderOnTop(true); 184 mRenderedView.setVisibility(View.VISIBLE); 185 }); 186 } 187 188 @Override 189 public void onError(Exception e) { 190 Trace.endAsyncSection( 191 "OdpClient:makeRequest:odpManager.requestSurfacePackage", 0); 192 makeToast("requestSurfacePackage() error: " + e.toString()); 193 } 194 }); 195 } catch (Exception e) { 196 Log.e(TAG, "Error", e); 197 } 198 } 199 registerScheduleTrainingButton()200 private void registerScheduleTrainingButton() { 201 mScheduleTrainingButton.setOnClickListener( 202 v -> scheduleTraining()); 203 } 204 scheduleTraining()205 private void scheduleTraining() { 206 try { 207 var odpManager = getOdpManager(); 208 CountDownLatch latch = new CountDownLatch(1); 209 Log.i( 210 TAG, 211 "Starting execute() " 212 + getResources().getString(R.string.schedule_training) 213 + " with " 214 + mScheduleTrainingTextBox.getHint().toString() 215 + ": " 216 + mScheduleTrainingTextBox.getText().toString()); 217 PersistableBundle appParams = new PersistableBundle(); 218 appParams.putString("schedule_training", mScheduleTrainingTextBox.getText().toString()); 219 if (mScheduleIntervalTextBox.getText() != null 220 && mScheduleIntervalTextBox.getText().toString() != null 221 && !mScheduleIntervalTextBox.getText().toString().isBlank()) { 222 appParams.putLong( 223 "schedule_interval", 224 Long.parseUnsignedLong(mScheduleIntervalTextBox.getText().toString())); 225 } 226 227 Trace.beginAsyncSection("OdpClient:scheduleTraining:odpManager.execute", 0); 228 odpManager.execute( 229 ComponentName.createRelative( 230 SERVICE_PACKAGE, 231 SERVICE_CLASS), 232 appParams, 233 sCallbackExecutor, 234 new OutcomeReceiver<ExecuteResult, Exception>() { 235 @Override 236 public void onResult(ExecuteResult result) { 237 Trace.endAsyncSection( 238 "OdpClient:scheduleTraining:odpManager.execute", 0); 239 Log.i(TAG, "execute() success: " + result); 240 latch.countDown(); 241 } 242 243 @Override 244 public void onError(Exception e) { 245 Trace.endAsyncSection( 246 "OdpClient:scheduleTraining:odpManager.execute", 0); 247 makeToast("execute() error: " + e.toString()); 248 latch.countDown(); 249 } 250 }); 251 latch.await(); 252 Log.d(TAG, "scheduleTraining:odpManager.execute wait success"); 253 } catch (Exception e) { 254 Log.e(TAG, "Error", e); 255 } 256 } 257 registerCancelTrainingButton()258 private void registerCancelTrainingButton() { 259 mCancelTrainingButton.setOnClickListener( 260 v -> cancelTraining()); 261 } 262 cancelTraining()263 private void cancelTraining() { 264 Log.d(TAG, "Odp Client Cancel Training called!"); 265 try { 266 var odpManager = getOdpManager(); 267 CountDownLatch latch = new CountDownLatch(1); 268 Log.i(TAG, "Starting execute() " + getResources().getString(R.string.cancel_training) 269 + " with " + mScheduleTrainingTextBox.getHint().toString() + ": " 270 + mScheduleTrainingTextBox.getText().toString()); 271 PersistableBundle appParams = new PersistableBundle(); 272 appParams.putString("cancel_training", mScheduleTrainingTextBox.getText().toString()); 273 274 Trace.beginAsyncSection("OdpClient:cancelTraining:odpManager.execute", 0); 275 odpManager.execute( 276 ComponentName.createRelative( 277 SERVICE_PACKAGE, 278 SERVICE_CLASS), 279 appParams, 280 sCallbackExecutor, 281 new OutcomeReceiver<ExecuteResult, Exception>() { 282 @Override 283 public void onResult(ExecuteResult result) { 284 Trace.endAsyncSection( 285 "OdpClient:cancelTraining:odpManager.execute", 0); 286 Log.i(TAG, "execute() success: " + result); 287 latch.countDown(); 288 } 289 290 @Override 291 public void onError(Exception e) { 292 Trace.endAsyncSection( 293 "OdpClient:cancelTraining:odpManager.execute", 0); 294 makeToast("execute() error: " + e.toString()); 295 latch.countDown(); 296 } 297 }); 298 latch.await(); 299 Log.d(TAG, "cancelTraining:odpManager.execute wait success"); 300 } catch (Exception e) { 301 Log.e(TAG, "Error", e); 302 } 303 } 304 reportConversion()305 private void reportConversion() { 306 try { 307 var odpManager = getOdpManager(); 308 CountDownLatch latch = new CountDownLatch(1); 309 Log.i(TAG, "Starting execute() " + getResources().getString(R.string.report_conversion) 310 + " with " + mReportConversionTextBox.getHint().toString() + ": " 311 + mReportConversionTextBox.getText().toString()); 312 PersistableBundle appParams = new PersistableBundle(); 313 appParams.putString("conversion_ad_id", mReportConversionTextBox.getText().toString()); 314 315 Trace.beginAsyncSection("OdpClient:reportConversion:odpManager.execute", 0); 316 odpManager.execute( 317 ComponentName.createRelative( 318 SERVICE_PACKAGE, 319 SERVICE_CLASS), 320 appParams, 321 sCallbackExecutor, 322 new OutcomeReceiver<ExecuteResult, Exception>() { 323 @Override 324 public void onResult(ExecuteResult result) { 325 Trace.endAsyncSection( 326 "OdpClient:reportConversion:odpManager.execute", 0); 327 Log.i(TAG, "execute() success: " + result); 328 latch.countDown(); 329 } 330 331 @Override 332 public void onError(Exception e) { 333 Trace.endAsyncSection( 334 "OdpClient:reportConversion:odpManager.execute", 0); 335 makeToast("execute() error: " + e.toString()); 336 latch.countDown(); 337 } 338 }); 339 latch.await(); 340 Log.d(TAG, "reportConversion:odpManager.execute wait success"); 341 } catch (Exception e) { 342 Log.e(TAG, "Error", e); 343 } 344 } 345 makeToast(String message)346 private void makeToast(String message) { 347 Log.i(TAG, message); 348 runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show()); 349 } 350 351 @Override onPause()352 public void onPause() { 353 Log.d(TAG, "onPause"); 354 super.onPause(); 355 } 356 @Override onSaveInstanceState(Bundle outState)357 public void onSaveInstanceState(Bundle outState) { 358 Log.d(TAG, "onSaveInstanceState"); 359 super.onSaveInstanceState(outState); 360 } 361 362 @Override onDestroy()363 public void onDestroy() { 364 Log.d(TAG, "onDestroy"); 365 super.onDestroy(); 366 } 367 368 @Override onRestoreInstanceState(Bundle savedInstanceState)369 public void onRestoreInstanceState(Bundle savedInstanceState) { 370 Log.d(TAG, "onRestoreInstanceState"); 371 super.onRestoreInstanceState(savedInstanceState); 372 } 373 374 @Override onResume()375 public void onResume() { 376 Log.d(TAG, "onResume"); 377 super.onResume(); 378 } 379 380 @Override onConfigurationChanged(Configuration newConfig)381 public void onConfigurationChanged(Configuration newConfig) { 382 Log.d(TAG, "onConfigurationChanged"); 383 super.onConfigurationChanged(newConfig); 384 } 385 printDebuggingInfo()386 private void printDebuggingInfo() { 387 printPackageVersion(getPackageName()); 388 printPackageVersion(SERVICE_PACKAGE); 389 printApexVersion(ODP_APEX); 390 printApexVersion(ADSERVICES_APEX); 391 } 392 printPackageVersion(String packageName)393 private void printPackageVersion(String packageName) { 394 try { 395 PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0); 396 String versionName = packageInfo.versionName; 397 Log.i(TAG, "packageName: " + packageName + ", versionName: " + versionName); 398 } catch (PackageManager.NameNotFoundException e) { 399 Log.e(TAG, "can't find package name " + packageName); 400 } 401 } 402 printApexVersion(String apexName)403 private void printApexVersion(String apexName) { 404 try { 405 PackageInfo apexInfo = 406 getPackageManager().getPackageInfo(apexName, PackageManager.MATCH_APEX); 407 if (apexInfo != null && apexInfo.isApex) { 408 Long apexVersionCode = apexInfo.getLongVersionCode(); 409 Log.i(TAG, "apexName: " + apexName + ", longVersionCode: " + apexVersionCode); 410 } 411 } catch (PackageManager.NameNotFoundException e) { 412 Log.e(TAG, "apex " + apexName + " not found"); 413 } 414 } 415 416 } 417