Calling a Java Class from PowerBuilder 10

本文介绍如何从PowerBuilder应用程序中调用独立的Java类,包括处理异常和将返回值传回PowerBuilder的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Calling a Java Class from PowerBuilder 10

Introduction

 
This whitepaper discusses how to call a stand-alone Java class from within a PowerBuilder application, including how to deal with exceptions and returning values back to PowerBuilder from the Java class.

Getting Started

Before you can start communicating with java classes from PowerBuilder you must add a file to your library list.  This file is pbejbclient100.pbd and it can typically be found in C:/Program Files/Sybase/Shared/PowerBuilder/.  Once you have this file in your library list you will be able to declare and create the Java VM and EJBConnection classes that are required to talk to a Java class.

At runtime PowerBuilder must also be able to find two other files, jvm.dll and pbjvm100.dll.

Creating the Java Class

There’s nothing special you need to do when creating the Java classes that you wish to access, there is however some issues you need to consider which are covered in Handling Exceptions and Returning Arguments.

To start with I have created a simple Java class with one method that will add two ints passed as arguments and return the result also as an int to demonstrate the use of Java in PowerBuilder:
public class JavaPBTest {

 

    public int add( int _number1, int _number2 )  {

        return _number1 + _number2;

    }
}

 
 

Before we can instantiate the java class and call it from PowerBuilder, we must fool PowerBuilder into thinking we are using an EJB, therefore we have to create a dummy home interface for the class.  The home interface for my class would look like this:

public interface JavaPBTestHome{}

We can now compile our two Java classes and move on to the next stage.

Creating the PowerBuilder Code

Creating a Proxy

Before we can instantiate our Java class in PowerScript we must create a proxy object for the java class. This is achieved by creating an EJB Client Proxy object (you can also use the wizard):
Once you have the proxy object you must specify the properties for the object using the Select Objects and Properties toolbar buttons (Edit..Select Objects and Edit..Properties menu items).
The Select Objects option is where you configure how PowerBuilder is to find your Java class and using our simple Java class we would enter the following data:

 
 

Note that the fully qualified name is the name including the package name so if we changed our Java class to reside in the package au.com.sybase i.e.

package au.com.sybase;

public class JavaPBTest {

    public int add( int _number1, int _number2 ) throws Exception {

        if( _number1 == 0 || _number2 == 0 )    {

            throw new Exception( "Argument is zero" );

        }
        else    {
        return _number1 + _number2;
        }
    }
}
Then we need to also change the fully qualified class name and interface class name to:

 

The next step is to specify which library the various objects that PowerBuilder will create as part of the proxy process should be created in.  This is done in Properties:
 

Once you have specified these options, choose Deploy to create the proxy, the process may take a minute or so.

Specifying a JDK

By default PowerBuilder will be installed with a version of Java and this can be used if desired.  However you may also want to choose a different JDK and this can be done using the Set JDK Location button of the Java tab of System Options:
 
As I’m using the default JDK to compile my class I have kept the default settings.
Note that you have to close down PowerBuilder before any changes to the JDK will take effect.

 

Writing the PowerScript

Now that we have the proxy we can create the PowerScript to instantiate and call our object.  In order to do this we must do the following things

    Create an instance of the Java VM (providing class path if not already set for the VM)
    Create an instance of the EJBConnection class
    Create the instance of the Java class

 
The code below shows an example of the PowerScript required to achieve the above:
 
INTEGER  li_result
INTEGER  li_return
EJBConnection  l_EJBConnection
JavaVM   l_jvm
javapbtest  l_javapbtest

 

// Create an instance of JavaVM class

l_jvm = CREATE JavaVM

// Start the JavaVM

li_return = l_jvm.createJavaVM( "C:/PBJAVA", FALSE )

// Check the return code

CHOOSE CASE li_return

    CASE 0, 1

    // OK

    CASE -1

        MessageBox( "Unable to Create JVM", "jvm.dll was not found in the classpath." )

    CASE -2

        MessageBox( "Unable to Create JVM", "pbejbclient90.jar file was not found." )

    CASE ELSE

        MessageBox( "Unable to Create JVM", "Unknown error." )

END CHOOSE

IF li_return = 0 OR li_return = 1 THEN

    // JavaVM created - Create EJBConnection

    l_EJBConnection = CREATE EJBConnection
    // Instantaite the Java class.
    li_return = l_EJBConnection.createJavaInstance( l_javapbtest, "javapbtest" )
    CHOOSE CASE li_return
        CASE -1
            MessageBox( "Can’t Create Java Class", "Failed to create Java class.")
        CASE -2
            MessageBox( "Can’t Create Java Class", " Invalid proxy name.")
        CASE -3
            MessageBox( "Can’t Create Java Class", "Failed to create proxy object.")
        CASE 0
            // All ok, call Java class method.
            li_result = l_javapbtest.add( 50, 60 )
            messagebox( "Java Result", STRING(li_result ) )
    END CHOOSE
END IF

// Clean up

DESTROY l_EJBConnection

DESTROY l_jvm
DESTROY l_javapbtest

Handling Exceptions and Returning Arguments

My simple Java class is currently returning an argument and PowerBuilder will happily deal with this and translate Java types to PowerBuilder types.  PowerBuilder will also handle exceptions and convert them to PowerBuilder exceptions so we could expand our Java class as follows:
 
public class JavaPBTest {
 
    public int add( int _number1, int _number2 ) throws Exception     {
        if( _number1 == 0 || _number2 == 0 )    {
            throw new Exception( "Argument is zero" );

        }

        else    {

            return _number1 + _number2;

        }

    }

}
 
 

To catch and handle the exception we expand our PowerBuilder code to include a Try and Catch block as follows:

TRY

    li_result = l_javapbtest.add( 0, 0 )

    messagebox( "Java Result", STRING(li_result ) )

CATCH( Throwable t )

    MessageBox( "method call failed", t.getMessage() )

END TRY

Changing the Java Class

Overtime we may change our class including adding new methods or perhaps just changing the body of a method.  If you change a method definition or add new methods then you will need to re-create the proxy objects using the proxy project you have created.
You can change the body of a method (not the definition), recompile and re-deploy the class without recreating the proxy objects, but in development you would need to shutdown and restart PowerBuilder before the new version of the class would be executed.

package com.android.server.power; 19 20 import android.app.ActivityManagerInternal; 21 import android.app.AlertDialog; 22 import android.app.BroadcastOptions; 23 import android.app.Dialog; 24 import android.app.IActivityManager; 25 import android.app.ProgressDialog; 26 import android.app.admin.SecurityLog; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.IIntentReceiver; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.PackageManagerInternal; 34 import android.content.pm.ActivityInfo; 35 import android.os.Bundle; 36 import android.os.FileUtils; 37 import android.os.Handler; 38 import android.os.PowerManager; 39 import android.os.RecoverySystem; 40 import android.os.RemoteException; 41 import android.os.ServiceManager; 42 import android.os.SystemClock; 43 import android.os.SystemProperties; 44 import android.os.SystemVibrator; 45 import android.os.Trace; 46 import android.os.UserHandle; 47 import android.os.UserManager; 48 import android.os.VibrationAttributes; 49 import android.os.VibrationEffect; 50 import android.os.Vibrator; 51 import android.os.vibrator.persistence.VibrationXmlParser; 52 import android.telephony.TelephonyManager; 53 import android.text.TextUtils; 54 import android.util.ArrayMap; 55 import android.util.Log; 56 import android.util.Slog; 57 import android.util.TimingsTraceLog; 58 import android.view.SurfaceControl; 59 import android.view.WindowManager; 60 import android.view.IWindowManager; 61 import android.view.Surface; 62 import com.android.server.pm.OplusDLCUtils; 63 64 //#ifdef OPLUS_FEATURE_SHUTDOWN_DETECT 65 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 66 import java.io.FileWriter; 67 import libcore.io.IoUtils; 68 //#endif /* OPLUS_FEATURE_SHUTDOWN_DETECT */ 69 70 import com.android.internal.annotations.VisibleForTesting; 71 import com.android.server.LocalServices; 72 import com.android.server.RescueParty; 73 import com.android.server.statusbar.StatusBarManagerInternal; 74 75 import java.io.File; 76 import java.io.FileOutputStream; 77 import java.io.FileReader; 78 import java.io.IOException; 79 import java.nio.charset.StandardCharsets; 80 81 public final class ShutdownThread extends Thread { 82 // constants 83 private static final boolean DEBUG = true; 84 private static final String TAG = "ShutdownThread"; 85 private static final int ACTION_DONE_POLL_WAIT_MS = 500; 86 private static final int RADIOS_STATE_POLL_SLEEP_MS = 100; 87 // maximum time we wait for the shutdown broadcast before going on. 88 private static final int MAX_BROADCAST_TIME = 10 * 1000; 89 private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 10 * 1000; 90 private static final int MAX_RADIO_WAIT_TIME = 12 * 1000; 91 private static final int MAX_UNCRYPT_WAIT_TIME = 15 * 60 * 1000; 92 // constants for progress bar. the values are roughly estimated based on timeout. 93 private static final int BROADCAST_STOP_PERCENT = 2; 94 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4; 95 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6; 96 private static final int RADIO_STOP_PERCENT = 18; 97 private static final int MOUNT_SERVICE_STOP_PERCENT = 20; 98 // Time we should wait for vendor subsystem shutdown 99 // Sleep times(ms) between checks of the vendor subsystem state 100 private static final int VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS = 100; 101 // Max time we wait for vendor subsystems to shut down before resuming 102 // with full system shutdown 103 private static final int VENDOR_SUBSYS_MAX_WAIT_MS = 10000; 104 105 // length of vibration before shutting down 106 @VisibleForTesting static final int DEFAULT_SHUTDOWN_VIBRATE_MS = 500; 107 108 // state tracking 109 private static final Object sIsStartedGuard = new Object(); 110 private static boolean sIsStarted = false; 111 112 private static boolean mReboot; 113 private static boolean mRebootSafeMode; 114 private static boolean mRebootHasProgressBar; 115 private static String mReason; 116 117 // Provides shutdown assurance in case the system_server is killed 118 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; 119 120 // Indicates whether we are rebooting into safe mode 121 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode"; 122 public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode"; 123 124 // static instance of this thread 125 private static final ShutdownThread sInstance = new ShutdownThread(); 126 127 // Metrics that will be reported to tron after reboot 128 private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>(); 129 130 // File to use for saving shutdown metrics 131 private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics"; 132 // File to use for saving shutdown check points 133 private static final String CHECK_POINTS_FILE_BASENAME = 134 "/data/system/shutdown-checkpoints/checkpoints"; 135 136 private static final int MIN_SHUTDOWN_ANIMATION_PLAY_TIME = 5*1000; 137 private static long beginAnimationTime = 0; 138 private static long endAnimationTime = 0; 139 140 // Metrics names to be persisted in shutdown-metrics file 141 private static String METRIC_SYSTEM_SERVER = "shutdown_system_server"; 142 private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast"; 143 private static String METRIC_AM = "shutdown_activity_manager"; 144 private static String METRIC_PM = "shutdown_package_manager"; 145 private static String METRIC_RADIOS = "shutdown_radios"; 146 private static String METRIC_RADIO = "shutdown_radio"; 147 private static String METRIC_SHUTDOWN_TIME_START = "begin_shutdown"; 148 149 private final Injector mInjector; 150 151 private final Object mActionDoneSync = new Object(); 152 private boolean mActionDone; 153 private Context mContext; 154 private PowerManager mPowerManager; 155 private PowerManager.WakeLock mCpuWakeLock; 156 private PowerManager.WakeLock mScreenWakeLock; 157 private Handler mHandler; 158 159 private static AlertDialog sConfirmDialog; 160 private ProgressDialog mProgressDialog; 161 162 private static final String PROP_IS_MONKEYKING_RUNNING = "oplus.autotest.monkeyRunning"; 163 private static final String PROP_IS_MONKEY_RUNNING = "oppo.autotest.monkeyRunning"; 164 private static final String PROP_IS_AGINGVERSION = "ro.build.aging_version"; 165 166 private ShutdownThread() { 167 this(new Injector()); 168 } 169 170 @VisibleForTesting 171 ShutdownThread(Injector injector) { 172 mInjector = injector; 173 } 174 175 /** 176 * Request a clean shutdown, waiting for subsystems to clean up their 177 * state etc. Must be called from a Looper thread in which its UI 178 * is shown. 179 * 180 * @param context Context used to display the shutdown progress dialog. This must be a context 181 * suitable for displaying UI (aka Themable). 182 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. 183 * @param confirm true if user confirmation is needed before shutting down. 184 */ 185 public static void shutdown(final Context context, String reason, boolean confirm) { 186 mReboot = false; 187 mRebootSafeMode = false; 188 mReason = reason; 189 shutdownInner(context, confirm); 190 } 191 192 private static void shutdownInner(final Context context, boolean confirm) { 193 // ShutdownThread is called from many places, so best to verify here that the context passed 194 // in is themed. 195 context.assertRuntimeOverlayThemable(); 196 197 // ensure that only one thread is trying to power down. 198 // any additional calls are just returned 199 synchronized (sIsStartedGuard) { 200 if (sIsStarted) { 201 if (DEBUG) { 202 Log.d(TAG, "Request to shutdown already running, returning."); 203 } 204 return; 205 } 206 } 207 208 // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but 209 // this point preserves the system trace of the trigger point of the ShutdownThread. 210 ShutdownCheckPoints.recordCheckPoint(/* reason= */ null); 211 212 //#ifdef ODM_WT_EDIT Gaohan.Wang@ODM_WT.AD, 2023/12/6 Add for Monkey call emergency, rebootPhone....... 213 if (isAgingTestNotAllowShutdown(mReason)) { 214 return; 215 } 216 //#endif ODM_WT_EDIT 217 218 final int longPressBehavior = context.getResources().getInteger( 219 com.android.internal.R.integer.config_longPressOnPowerBehavior); 220 final int resourceId = mRebootSafeMode 221 ? com.android.internal.R.string.reboot_safemode_confirm 222 : (longPressBehavior == 2 223 ? com.android.internal.R.string.shutdown_confirm_question 224 : com.android.internal.R.string.shutdown_confirm); 225 226 if (DEBUG) { 227 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); 228 } 229 230 if (confirm) { 231 final CloseDialogReceiver closer = new CloseDialogReceiver(context); 232 if (sConfirmDialog != null) { 233 sConfirmDialog.dismiss(); 234 } 235 sConfirmDialog = new AlertDialog.Builder(context) 236 .setTitle(mRebootSafeMode 237 ? com.android.internal.R.string.reboot_safemode_title 238 : com.android.internal.R.string.power_off) 239 .setMessage(resourceId) 240 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { 241 public void onClick(DialogInterface dialog, int which) { 242 beginShutdownSequence(context); 243 } 244 }) 245 .setNegativeButton(com.android.internal.R.string.no, null) 246 .create(); 247 closer.dialog = sConfirmDialog; 248 sConfirmDialog.setOnDismissListener(closer); 249 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 250 sConfirmDialog.show(); 251 } else { 252 beginShutdownSequence(context); 253 } 254 } 255 256 public static boolean isAgingTestNotAllowShutdown(String reason) { 257 boolean isAgingTest = "yes".equals(SystemProperties.get(PROP_IS_AGINGVERSION)); 258 boolean isMonkeyRunning = SystemProperties.getBoolean(PROP_IS_MONKEY_RUNNING, false); 259 boolean isMonkeyKingRunning = SystemProperties.getBoolean(PROP_IS_MONKEYKING_RUNNING, false); 260 Slog.d(TAG, " reason=" + reason 261 + ", isAgingTest=" + isAgingTest 262 + ", isMonkeyRunning=" + isMonkeyRunning 263 + ", isMonkeyKingRunning=" + isMonkeyKingRunning); 264 if (isAgingTest && (isMonkeyRunning || isMonkeyKingRunning) 265 && !PowerManager.SHUTDOWN_LOW_BATTERY.equals(reason)) { 266 Slog.d(TAG, "AgingTestNotAllowShutdown"); 267 return true; 268 } 269 if (isAgingTest && "silent".equals(reason)) { 270 Slog.d(TAG, "AgingTestNotAllowShutdown silence reboot"); 271 return true; 272 } 273 return false; 274 } 275 276 private static class CloseDialogReceiver extends BroadcastReceiver 277 implements DialogInterface.OnDismissListener { 278 private Context mContext; 279 public Dialog dialog; 280 281 CloseDialogReceiver(Context context) { 282 mContext = context; 283 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 284 context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED); 285 } 286 287 @Override 288 public void onReceive(Context context, Intent intent) { 289 dialog.cancel(); 290 } 291 292 public void onDismiss(DialogInterface unused) { 293 mContext.unregisterReceiver(this); 294 } 295 } 296 297 /** 298 * Request a clean shutdown, waiting for subsystems to clean up their 299 * state etc. Must be called from a Looper thread in which its UI 300 * is shown. 301 * 302 * @param context Context used to display the shutdown progress dialog. This must be a context 303 * suitable for displaying UI (aka Themable). 304 * @param reason code to pass to the kernel (e.g. "recovery"), or null. 305 * @param confirm true if user confirmation is needed before shutting down. 306 */ 307 public static void reboot(final Context context, String reason, boolean confirm) { 308 mReboot = true; 309 mRebootSafeMode = false; 310 mRebootHasProgressBar = false; 311 mReason = reason; 312 shutdownInner(context, confirm); 313 } 314 315 /** 316 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI 317 * is shown. 318 * 319 * @param context Context used to display the shutdown progress dialog. This must be a context 320 * suitable for displaying UI (aka Themable). 321 * @param confirm true if user confirmation is needed before shutting down. 322 */ 323 public static void rebootSafeMode(final Context context, boolean confirm) { 324 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 325 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { 326 return; 327 } 328 329 // #ifdef dongzhenbo@ANDROID.FRAMEWORK, modified for India DLC 330 if (!isDeviceProvisioned(context) && OplusDLCUtils.isDLCVersion()) { 331 return; 332 } 333 // endif 334 335 mReboot = true; 336 mRebootSafeMode = true; 337 mRebootHasProgressBar = false; 338 mReason = null; 339 shutdownInner(context, confirm); 340 } 341 342 // #ifdef dongzhenbo@ANDROID.FRAMEWORK, modified for India DLC 343 private static boolean isDeviceProvisioned(Context context) { 344 return android.provider.Settings.Global.getInt(context.getContentResolver(), 345 android.provider.Settings.Global.DEVICE_PROVISIONED, 0) != 0; 346 } 347 // endif 348 349 private static ProgressDialog showShutdownDialog(Context context) { 350 // Throw up a system dialog to indicate the device is rebooting / shutting down. 351 ProgressDialog pd = new ProgressDialog(context); 352 353 // Path 1: Reboot to recovery for update 354 // Condition: mReason startswith REBOOT_RECOVERY_UPDATE 355 // 356 // Path 1a: uncrypt needed 357 // Condition: if /cache/recovery/uncrypt_file exists but 358 // /cache/recovery/block.map doesn't. 359 // UI: determinate progress bar (mRebootHasProgressBar == True) 360 // 361 // * Path 1a is expected to be removed once the GmsCore shipped on 362 // device always calls uncrypt prior to reboot. 363 // 364 // Path 1b: uncrypt already done 365 // UI: spinning circle only (no progress bar) 366 // 367 // Path 2: Reboot to recovery for factory reset 368 // Condition: mReason == REBOOT_RECOVERY 369 // UI: spinning circle only (no progress bar) 370 // 371 // Path 3: Regular reboot / shutdown 372 // Condition: Otherwise 373 // UI: spinning circle only (no progress bar) 374 375 // mReason could be "recovery-update" or "recovery-update,quiescent". 376 if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { 377 // We need the progress bar if uncrypt will be invoked during the 378 // reboot, which might be time-consuming. 379 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists() 380 && !(RecoverySystem.BLOCK_MAP_FILE.exists()); 381 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); 382 if (mRebootHasProgressBar) { 383 pd.setMax(100); 384 pd.setProgress(0); 385 pd.setIndeterminate(false); 386 boolean showPercent = context.getResources().getBoolean( 387 com.android.internal.R.bool.config_showPercentageTextDuringRebootToUpdate); 388 if (!showPercent) { 389 pd.setProgressPercentFormat(null); 390 } 391 pd.setProgressNumberFormat(null); 392 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 393 pd.setMessage(context.getText( 394 com.android.internal.R.string.reboot_to_update_prepare)); 395 } else { 396 if (showSysuiReboot()) { 397 return null; 398 } 399 pd.setIndeterminate(true); 400 pd.setMessage(context.getText( 401 com.android.internal.R.string.reboot_to_update_reboot)); 402 } 403 } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) { 404 if (RescueParty.isRecoveryTriggeredReboot()) { 405 // We're not actually doing a factory reset yet; we're rebooting 406 // to ask the user if they'd like to reset, so give them a less 407 // scary dialog message. 408 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 409 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 410 pd.setIndeterminate(true); 411 } else if (showSysuiReboot()) { 412 return null; 413 } else { 414 // Factory reset path. Set the dialog message accordingly. 415 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); 416 pd.setMessage(context.getText( 417 com.android.internal.R.string.reboot_to_reset_message)); 418 pd.setIndeterminate(true); 419 } 420 } else { 421 if (showSysuiReboot()) { 422 return null; 423 } 424 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 425 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 426 pd.setIndeterminate(true); 427 } 428 pd.setCancelable(false); 429 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 430 if(isPreVersion()){ 431 pd.show(); 432 } 433 return pd; 434 } 435 436 private static void showShutdownAnimation() { 437 Log.w(TAG, "showShutdownAnimation"); 438 SystemProperties.set("service.bootanim.exit", "0"); 439 SystemProperties.set("ctl.start", "banim_shut"); 440 } 441 442 private static boolean isPreVersion(){ 443 return "true".equals(SystemProperties.get("ro.build.oplus.pre_version")); 444 } 445 446 private static void delayForPlayAnimation() { 447 Log.i(TAG,"set service.shutanim.running to 1"); 448 if(beginAnimationTime <= 0){ 449 return; 450 } 451 endAnimationTime = beginAnimationTime-SystemClock.elapsedRealtime(); 452 if(endAnimationTime > 0){ 453 try { 454 Log.w(TAG, "sleep delayForPlayAnimation"); 455 Thread.currentThread().sleep(1500); 456 } catch (InterruptedException e) { 457 Log.e(TAG, "Shutdown stop bootanimation Thread.currentThread().sleep exception!"); 458 } 459 } 460 } 461 462 private static boolean showSysuiReboot() { 463 if (DEBUG) { 464 Log.d(TAG, "Attempting to use SysUI shutdown UI"); 465 } 466 try { 467 StatusBarManagerInternal service = LocalServices.getService( 468 StatusBarManagerInternal.class); 469 if (service.showShutdownUi(mReboot, mReason)) { 470 // Sysui will handle shutdown UI. 471 if (DEBUG) { 472 Log.d(TAG, "SysUI handling shutdown UI"); 473 } 474 return true; 475 } 476 } catch (Exception e) { 477 // If anything went wrong, ignore it and use fallback ui 478 } 479 if (DEBUG) { 480 Log.d(TAG, "SysUI is unavailable"); 481 } 482 return false; 483 } 484 485 private static void beginShutdownSequence(Context context) { 486 synchronized (sIsStartedGuard) { 487 if (sIsStarted) { 488 if (DEBUG) { 489 Log.d(TAG, "Shutdown sequence already running, returning."); 490 } 491 return; 492 } 493 sIsStarted = true; 494 } 495 if(isPreVersion()){ 496 sInstance.mProgressDialog = showShutdownDialog(context); 497 } else { 498 beginAnimationTime = SystemClock.elapsedRealtime()+MIN_SHUTDOWN_ANIMATION_PLAY_TIME; 499 showShutdownAnimation(); 500 } 501 502 sInstance.mContext = context; 503 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 504 505 // make sure we never fall asleep again 506 sInstance.mCpuWakeLock = null; 507 try { 508 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( 509 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); 510 sInstance.mCpuWakeLock.setReferenceCounted(false); 511 sInstance.mCpuWakeLock.acquire(); 512 } catch (SecurityException e) { 513 Log.w(TAG, "No permission to acquire wake lock", e); 514 sInstance.mCpuWakeLock = null; 515 } 516 517 // also make sure the screen stays on for better user experience 518 sInstance.mScreenWakeLock = null; 519 if (sInstance.mPowerManager.isScreenOn()) { 520 try { 521 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( 522 PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); 523 sInstance.mScreenWakeLock.setReferenceCounted(false); 524 sInstance.mScreenWakeLock.acquire(); 525 } catch (SecurityException e) { 526 Log.w(TAG, "No permission to acquire wake lock", e); 527 sInstance.mScreenWakeLock = null; 528 } 529 } 530 531 if (SecurityLog.isLoggingEnabled()) { 532 SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); 533 } 534 delayForPlayAnimation(); 535 536 // start the thread that initiates shutdown 537 sInstance.mHandler = new Handler() { 538 }; 539 sInstance.start(); 540 } 541 542 void actionDone() { 543 synchronized (mActionDoneSync) { 544 mActionDone = true; 545 mActionDoneSync.notifyAll(); 546 } 547 } 548 549 //#ifdef OPLUS_FEATURE_SHUTDOWN_DETECT 550 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 551 /** 552 * @return void doShutdownDetect 553 */ 554 private static void doShutdownDetect(String count) { 555 File procFile = null; 556 FileWriter shutdown_detect = null; 557 try { 558 Log.e(TAG, "doShutdownDetect " + count); 559 procFile = new File("/proc/shutdown_detect"); 560 shutdown_detect = new FileWriter(procFile); 561 shutdown_detect.write(count); 562 } catch (java.io.IOException e) { 563 Log.w(TAG, "Failed to write to /proc/shutdown_detect", e); 564 } finally { 565 IoUtils.closeQuietly(shutdown_detect); 566 } 567 } 568 //#endif /* OPLUS_FEATURE_SHUTDOWN_DETECT */ 569 570 /** 571 * Makes sure we handle the shutdown gracefully. 572 * Shuts off power regardless of radio state if the allotted time has passed. 573 */ 574 public void run() { 575 TimingsTraceLog shutdownTimingLog = newTimingsLog(); 576 shutdownTimingLog.traceBegin("SystemServerShutdown"); 577 metricShutdownStart(); 578 metricStarted(METRIC_SYSTEM_SERVER); 579 580 //#ifdef OPLUS_EXTENSION_HOOK 581 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 582 doShutdownDetect("40"); 583 //#endif /* OPLUS_EXTENSION_HOOK */ 584 585 // Notify SurfaceFlinger that the device is shutting down. 586 // Transaction traces should be captured at this stage. 587 SurfaceControl.notifyShutdown(); 588 589 // Start dumping check points for this shutdown in a separate thread. 590 Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread( 591 new File(CHECK_POINTS_FILE_BASENAME)); 592 dumpCheckPointsThread.start(); 593 594 /* 595 * Write a system property in case the system_server reboots before we 596 * get to the actual hardware restart. If that happens, we'll retry at 597 * the beginning of the SystemServer startup. 598 */ 599 { 600 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); 601 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 602 } 603 604 /* 605 * If we are rebooting into safe mode, write a system property 606 * indicating so. 607 */ 608 if (mRebootSafeMode) { 609 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 610 } 611 612 shutdownTimingLog.traceBegin("DumpPreRebootInfo"); 613 try { 614 Slog.i(TAG, "Logging pre-reboot information..."); 615 PreRebootLogger.log(mContext); 616 } catch (Exception e) { 617 Slog.e(TAG, "Failed to log pre-reboot information", e); 618 } 619 shutdownTimingLog.traceEnd(); // DumpPreRebootInfo 620 621 metricStarted(METRIC_SEND_BROADCAST); 622 shutdownTimingLog.traceBegin("SendShutdownBroadcast"); 623 Log.i(TAG, "Sending shutdown broadcast..."); 624 625 // First send the high-level shut down broadcast. 626 mActionDone = false; 627 Intent intent = new Intent(Intent.ACTION_SHUTDOWN); 628 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); 629 final Bundle opts = BroadcastOptions.makeBasic() 630 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) 631 .toBundle(); 632 final ActivityManagerInternal activityManagerInternal = LocalServices.getService( 633 ActivityManagerInternal.class); 634 activityManagerInternal.broadcastIntentWithCallback(intent, 635 new IIntentReceiver.Stub() { 636 @Override 637 public void performReceive(Intent intent, int resultCode, String data, 638 Bundle extras, boolean ordered, boolean sticky, int sendingUser) { 639 mHandler.post(ShutdownThread.this::actionDone); 640 } 641 }, null, UserHandle.USER_ALL, null, null, opts); 642 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 643 synchronized (mActionDoneSync) { 644 while (!mActionDone) { 645 long delay = endTime - SystemClock.elapsedRealtime(); 646 if (delay <= 0) { 647 Log.w(TAG, "Shutdown broadcast timed out"); 648 //#ifdef OPLUS_EXTENSION_HOOK 649 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 650 doShutdownDetect("47"); 651 //#endif /* OPLUS_EXTENSION_HOOK */ 652 break; 653 } else if (mRebootHasProgressBar) { 654 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 * 655 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); 656 sInstance.setRebootProgress(status, null); 657 } 658 try { 659 mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); 660 } catch (InterruptedException e) { 661 } 662 } 663 } 664 if (mRebootHasProgressBar) { 665 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); 666 } 667 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast 668 metricEnded(METRIC_SEND_BROADCAST); 669 670 Log.i(TAG, "Shutting down activity manager..."); 671 shutdownTimingLog.traceBegin("ShutdownActivityManager"); 672 metricStarted(METRIC_AM); 673 //#ifdef OPLUS_EXTENSION_HOOK 674 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 675 long startTime = SystemClock.elapsedRealtime(); 676 //#endif /* OPLUS_EXTENSION_HOOK */ 677 678 final IActivityManager am = 679 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); 680 if (am != null) { 681 try { 682 am.shutdown(MAX_BROADCAST_TIME); 683 } catch (RemoteException e) { 684 } 685 } 686 if (mRebootHasProgressBar) { 687 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); 688 } 689 //#ifdef OPLUS_EXTENSION_HOOK 690 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 691 if((SystemClock.elapsedRealtime() - startTime) > MAX_BROADCAST_TIME) { 692 doShutdownDetect("46"); 693 } 694 //#endif /* OPLUS_EXTENSION_HOOK */ 695 696 shutdownTimingLog.traceEnd();// ShutdownActivityManager 697 metricEnded(METRIC_AM); 698 699 Log.i(TAG, "Shutting down package manager..."); 700 shutdownTimingLog.traceBegin("ShutdownPackageManager"); 701 metricStarted(METRIC_PM); 702 //#ifdef OPLUS_EXTENSION_HOOK 703 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 704 startTime = SystemClock.elapsedRealtime(); 705 //#endif /* OPLUS_EXTENSION_HOOK */ 706 707 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); 708 if (pm != null) { 709 pm.shutdown(); 710 } 711 if (mRebootHasProgressBar) { 712 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); 713 } 714 //#ifdef OPLUS_EXTENSION_HOOK 715 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 716 if((SystemClock.elapsedRealtime() - startTime) > MAX_BROADCAST_TIME) { 717 doShutdownDetect("45"); 718 } 719 //#endif /* OPLUS_EXTENSION_HOOK */ 720 721 shutdownTimingLog.traceEnd(); // ShutdownPackageManager 722 metricEnded(METRIC_PM); 723 724 // Shutdown radios. 725 shutdownTimingLog.traceBegin("ShutdownRadios"); 726 metricStarted(METRIC_RADIOS); 727 shutdownRadios(MAX_RADIO_WAIT_TIME); 728 if (mRebootHasProgressBar) { 729 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); 730 } 731 shutdownTimingLog.traceEnd(); // ShutdownRadios 732 metricEnded(METRIC_RADIOS); 733 734 if (mRebootHasProgressBar) { 735 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); 736 737 // If it's to reboot to install an update and uncrypt hasn't been 738 // done yet, trigger it now. 739 uncrypt(); 740 } 741 742 // Wait for the check points dump thread to finish, or kill it if not finished in time. 743 shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait"); 744 try { 745 dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME); 746 } catch (InterruptedException ex) { 747 } 748 Log.i(TAG, "run() shutdownAnimationService"); 749 750 shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait 751 752 shutdownTimingLog.traceEnd(); // SystemServerShutdown 753 metricEnded(METRIC_SYSTEM_SERVER); 754 saveMetrics(mReboot, mReason); 755 //#ifdef OPLUS_EXTENSION_HOOK 756 //ODM_WT.AD.Framework ZhangFei@BSP.KERNEL.STABILITY, 2020/04/17, Add for shutdown detect. 757 //Shutdown from Systemserver, set timeout 120s, 4*30. 758 doShutdownDetect("4"); 759 //#endif /* OPLUS_EXTENSION_HOOK */ 760 // Remaining work will be done by init, including vold shutdown 761 rebootOrShutdown(mContext, mReboot, mReason); 762 } 763 764 private static TimingsTraceLog newTimingsLog() { 765 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER); 766 } 767 768 private static void metricStarted(String metricKey) { 769 synchronized (TRON_METRICS) { 770 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime()); 771 } 772 } 773 774 private static void metricEnded(String metricKey) { 775 synchronized (TRON_METRICS) { 776 TRON_METRICS 777 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey)); 778 } 779 } 780 781 private static void metricShutdownStart() { 782 synchronized (TRON_METRICS) { 783 TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis()); 784 } 785 } 786 787 private void setRebootProgress(final int progress, final CharSequence message) { 788 mHandler.post(new Runnable() { 789 @Override 790 public void run() { 791 if (mProgressDialog != null) { 792 mProgressDialog.setProgress(progress); 793 if (message != null) { 794 mProgressDialog.setMessage(message); 795 } 796 } 797 } 798 }); 799 } 800 801 private void shutdownRadios(final int timeout) { 802 // If a radio is wedged, disabling it may hang so we do this work in another thread, 803 // just in case. 804 final long endTime = SystemClock.elapsedRealtime() + timeout; 805 final boolean[] done = new boolean[1]; 806 Thread t = new Thread() { 807 public void run() { 808 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog(); 809 boolean radioOff; 810 811 TelephonyManager telephonyManager = mContext.getSystemService( 812 TelephonyManager.class); 813 814 radioOff = telephonyManager == null 815 || !telephonyManager.isAnyRadioPoweredOn(); 816 if (!radioOff) { 817 Log.w(TAG, "Turning off cellular radios..."); 818 metricStarted(METRIC_RADIO); 819 telephonyManager.shutdownAllRadios(); 820 } 821 822 Log.i(TAG, "Waiting for Radio..."); 823 824 long delay = endTime - SystemClock.elapsedRealtime(); 825 while (delay > 0) { 826 if (mRebootHasProgressBar) { 827 int status = (int)((timeout - delay) * 1.0 * 828 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout); 829 status += PACKAGE_MANAGER_STOP_PERCENT; 830 sInstance.setRebootProgress(status, null); 831 } 832 833 if (!radioOff) { 834 radioOff = !telephonyManager.isAnyRadioPoweredOn(); 835 if (radioOff) { 836 Log.i(TAG, "Radio turned off."); 837 metricEnded(METRIC_RADIO); 838 shutdownTimingsTraceLog 839 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO)); 840 } 841 } 842 843 if (radioOff) { 844 Log.i(TAG, "Radio shutdown complete."); 845 done[0] = true; 846 break; 847 } 848 SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS); 849 delay = endTime - SystemClock.elapsedRealtime(); 850 } 851 } 852 }; 853 854 t.start(); 855 try { 856 t.join(timeout); 857 } catch (InterruptedException ex) { 858 } 859 if (!done[0]) { 860 Log.w(TAG, "Timed out waiting for Radio shutdown."); 861 } 862 } 863 864 /** 865 * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 866 * or {@link #shutdown(Context, String, boolean)} instead. 867 * 868 * @param context Context used to vibrate or null without vibration 869 * @param reboot true to reboot or false to shutdown 870 * @param reason reason for reboot/shutdown 871 */ 872 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { 873 String subsysProp; 874 subsysProp = SystemProperties.get("vendor.peripheral.shutdown_critical_list", 875 "ERROR"); 876 //If we don't have the shutdown critical subsystem list we can't 877 //really do anything. Proceed with full system shutdown. 878 if (!subsysProp.equals("ERROR")) { 879 Log.i(TAG, "Shutdown critical subsyslist is :"+subsysProp+": "); 880 Log.i(TAG, "Waiting for a maximum of " + 881 (VENDOR_SUBSYS_MAX_WAIT_MS) + "ms"); 882 String[] subsysList = subsysProp.split(" "); 883 int wait_count = 0; 884 boolean okToShutdown = true; 885 String subsysState; 886 int subsysListLength = subsysList.length; 887 do { 888 okToShutdown = true; 889 for (int i = 0; i < subsysListLength; i++) { 890 subsysState = 891 SystemProperties.get( 892 "vendor.peripheral." + 893 subsysList[i] + 894 ".state", 895 "ERROR"); 896 if(subsysState.equals("ONLINE")) { 897 //We only want to delay shutdown while 898 //one of the shutdown critical 899 //subsystems still shows as 'ONLINE'. 900 okToShutdown = false; 901 } 902 } 903 if (okToShutdown == false) { 904 SystemClock.sleep(VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS); 905 wait_count++; 906 } 907 } while (okToShutdown != true && 908 wait_count < (VENDOR_SUBSYS_MAX_WAIT_MS/VENDOR_SUBSYS_STATE_CHECK_INTERVAL_MS)); 909 if (okToShutdown != true) { 910 for (int i = 0; i < subsysList.length; i++) { 911 subsysState = 912 SystemProperties.get( 913 "vendor.peripheral." + 914 subsysList[i] + 915 ".state", 916 "ERROR"); 917 if(subsysState.equals("ONLINE")) { 918 Log.w(TAG, "Subsystem " + subsysList[i]+ 919 "did not shut down within timeout"); 920 } 921 } 922 //#ifdef OPLUS_EXTENSION_HOOK 923 //koulongfei@ODM_WT.AD.Framework, 2023/08/22, Add for shutdown detect. 924 doShutdownDetect("43"); 925 //#endif /* OPLUS_EXTENSION_HOOK */ 926 } else { 927 Log.i(TAG, "Vendor subsystem(s) shutdown successful"); 928 } 929 } 930 if (sInstance.mPowerManager != null){ 931 Log.i(TAG, "mPowerManager.goToSleep"); 932 sInstance.mPowerManager.goToSleep(SystemClock.uptimeMillis()); 933 } 934 if (reboot) { 935 Log.i(TAG, "Rebooting, reason: " + reason); 936 PowerManagerService.lowLevelReboot(reason); 937 Log.e(TAG, "Reboot failed, will attempt shutdown instead"); 938 reason = null; 939 } else if (context != null) { 940 // vibrate before shutting down 941 try { 942 sInstance.playShutdownVibration(context); 943 } catch (Exception e) { 944 // Failure to vibrate shouldn't interrupt shutdown. Just log it. 945 Log.w(TAG, "Failed to vibrate during shutdown.", e); 946 } 947 948 } 949 // Shutdown power 950 Log.i(TAG, "Performing low-level shutdown..."); 951 PowerManagerService.lowLevelShutdown(reason); 952 } 953 954 /** 955 * Plays a vibration for shutdown. Along with playing a shutdown vibration, this method also 956 * sleeps the current Thread for some time, to allow the vibration to finish before the device 957 * shuts down. 958 */ 959 @VisibleForTesting // For testing vibrations without shutting down device 960 void playShutdownVibration(Context context) { 961 Vibrator vibrator = mInjector.getVibrator(context); 962 if (!vibrator.hasVibrator()) { 963 return; 964 } 965 966 VibrationEffect vibrationEffect = getValidShutdownVibration(context, vibrator); 967 vibrator.vibrate( 968 vibrationEffect, 969 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH)); 970 971 // vibrator is asynchronous so we have to wait to avoid shutting down too soon. 972 long vibrationDuration = vibrationEffect.getDuration(); 973 // A negative vibration duration may indicate a vibration effect whose duration is not 974 // known by the system (e.g. pre-baked effects). In that case, use the default shutdown 975 // vibration duration. 976 mInjector.sleep(vibrationDuration < 0 ? DEFAULT_SHUTDOWN_VIBRATE_MS : vibrationDuration); 977 } 978 979 private static void saveMetrics(boolean reboot, String reason) { 980 StringBuilder metricValue = new StringBuilder(); 981 metricValue.append("reboot:"); 982 metricValue.append(reboot ? "y" : "n"); 983 metricValue.append(",").append("reason:").append(reason); 984 final int metricsSize = TRON_METRICS.size(); 985 for (int i = 0; i < metricsSize; i++) { 986 final String name = TRON_METRICS.keyAt(i); 987 final long value = TRON_METRICS.valueAt(i); 988 if (value < 0) { 989 Log.e(TAG, "metricEnded wasn't called for " + name); 990 continue; 991 } 992 metricValue.append(',').append(name).append(':').append(value); 993 } 994 File tmp = new File(METRICS_FILE_BASENAME + ".tmp"); 995 boolean saved = false; 996 try (FileOutputStream fos = new FileOutputStream(tmp)) { 997 fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8)); 998 saved = true; 999 } catch (IOException e) { 1000 Log.e(TAG,"Cannot save shutdown metrics", e); 1001 } 1002 if (saved) { 1003 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt")); 1004 } 1005 } 1006 1007 private void uncrypt() { 1008 Log.i(TAG, "Calling uncrypt and monitoring the progress..."); 1009 1010 final RecoverySystem.ProgressListener progressListener = 1011 new RecoverySystem.ProgressListener() { 1012 @Override 1013 public void onProgress(int status) { 1014 if (status >= 0 && status < 100) { 1015 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100). 1016 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100); 1017 status += MOUNT_SERVICE_STOP_PERCENT; 1018 CharSequence msg = mContext.getText( 1019 com.android.internal.R.string.reboot_to_update_package); 1020 sInstance.setRebootProgress(status, msg); 1021 } else if (status == 100) { 1022 CharSequence msg = mContext.getText( 1023 com.android.internal.R.string.reboot_to_update_reboot); 1024 sInstance.setRebootProgress(status, msg); 1025 } else { 1026 // Ignored 1027 } 1028 } 1029 }; 1030 1031 final boolean[] done = new boolean[1]; 1032 done[0] = false; 1033 Thread t = new Thread() { 1034 @Override 1035 public void run() { 1036 RecoverySystem rs = (RecoverySystem) mContext.getSystemService( 1037 Context.RECOVERY_SERVICE); 1038 String filename = null; 1039 try { 1040 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null); 1041 rs.processPackage(mContext, new File(filename), progressListener); 1042 } catch (IOException e) { 1043 Log.e(TAG, "Error uncrypting file", e); 1044 } 1045 done[0] = true; 1046 } 1047 }; 1048 t.start(); 1049 1050 try { 1051 t.join(MAX_UNCRYPT_WAIT_TIME); 1052 } catch (InterruptedException unused) { 1053 } 1054 if (!done[0]) { 1055 Log.w(TAG, "Timed out waiting for uncrypt."); 1056 final int uncryptTimeoutError = 100; 1057 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n", 1058 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError); 1059 try { 1060 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage); 1061 } catch (IOException e) { 1062 Log.e(TAG, "Failed to write timeout message to uncrypt status", e); 1063 } 1064 } 1065 } 1066 1067 /** 1068 * Provides a {@link VibrationEffect} to be used for shutdown. 1069 * 1070 * <p>The vibration to be played is derived from the shutdown vibration file (which the device 1071 * should specify at `com.android.internal.R.string.config_defaultShutdownVibrationFile`). A 1072 * fallback vibration maybe used in one of these conditions: 1073 * <ul> 1074 * <li>A vibration file has not been specified, or if the specified file does not exist 1075 * <li>If the content of the file does not represent a valid serialization of a 1076 * {@link VibrationEffect} 1077 * <li>If the {@link VibrationEffect} specified in the file is not suitable for 1078 * a shutdown vibration (such as indefinite vibrations) 1079 * </ul> 1080 */ 1081 private VibrationEffect getValidShutdownVibration(Context context, Vibrator vibrator) { 1082 VibrationEffect parsedEffect = parseVibrationEffectFromFile( 1083 mInjector.getDefaultShutdownVibrationEffectFilePath(context), 1084 vibrator); 1085 1086 if (parsedEffect == null) { 1087 return createDefaultVibrationEffect(); 1088 } 1089 1090 long parsedEffectDuration = parsedEffect.getDuration(); 1091 if (parsedEffectDuration == Long.MAX_VALUE) { 1092 // This means that the effect does not have a defined end. 1093 // Since we don't want to vibrate forever while trying to shutdown, we ignore this 1094 // parsed effect and use the default one instead. 1095 Log.w(TAG, "The parsed shutdown vibration is indefinite."); 1096 return createDefaultVibrationEffect(); 1097 } 1098 1099 return parsedEffect; 1100 } 1101 1102 private static VibrationEffect parseVibrationEffectFromFile( 1103 String filePath, Vibrator vibrator) { 1104 if (!TextUtils.isEmpty(filePath)) { 1105 try { 1106 return VibrationXmlParser.parseDocument(new FileReader(filePath)).resolve(vibrator); 1107 } catch (Exception e) { 1108 Log.e(TAG, "Error parsing default shutdown vibration effect.", e); 1109 } 1110 } 1111 return null; 1112 } 1113 1114 private static VibrationEffect createDefaultVibrationEffect() { 1115 return VibrationEffect.createOneShot( 1116 DEFAULT_SHUTDOWN_VIBRATE_MS, VibrationEffect.DEFAULT_AMPLITUDE); 1117 } 1118 1119 /** Utility class to inject instances, for easy testing. */ 1120 @VisibleForTesting 1121 static class Injector { 1122 public Vibrator getVibrator(Context context) { 1123 return new SystemVibrator(context); 1124 } 1125 1126 public void sleep(long durationMs) { 1127 try { 1128 Thread.sleep(durationMs); 1129 } catch (InterruptedException unused) { 1130 // this is not critical and does not require logging. 1131 } 1132 } 1133 1134 public String getDefaultShutdownVibrationEffectFilePath(Context context) { 1135 return context.getResources().getString( 1136 com.android.internal.R.string.config_defaultShutdownVibrationFile); 1137 } 1138 } 1139 }
最新发布
06-27
<think>Hmm,theuserisaskingaboutinvokingaprivateconstructoroftheJsonParserclassinC++orJava.Letmeanalyzethiscarefully.First,lookingatthecontext-theuserreferencedtwodocumentationsnippetsearlier,oneaboutJsonParsermethodsinJavaandanotheraboutNativeViewFactoryinKotlin.Butnowthey'veshiftedfocustoconstructoraccess.Interestingpivot-theymightbetryingtoextendormodifyparserbehavior.Thecorechallengeisclear:privateconstructorsaredesignedtopreventdirectinstantiation.Theuserlikelyknowsthisbutstillasks,sotheyprobablyhavealegitimatereasonliketestinglegacycodeorextendingclosedfunctionality.Ishouldaddressboththetechnicalsolutionandthedesignimplications.ForJavasolutions,Ineedtoemphasizethatreflectionistheprimarybutriskyapproach.Musthighlightthedangers:securitymanagersthrowingexceptions,accessissuesinmodules,andbreakingfuturecompatibility.Theexampleshouldshowprecisetry-catchblocksbecausereflectionerrorsareruntimeexceptions.Thefactorypatternmentioniscrucialthough-theuser'sownreferencetoJsonFactory.createParser()actuallyanswerstheirquestionindirectly.Theymighthaveoverlookedthatintheirfrustration.I'llexplicitlyconnectthatdocumentationsnippettothesolution.ForC++,thefriendclassapproachistheoreticallycleanerbutlesspracticalsincetheydon'tcontrolthelibrarysource.ShouldgentlysteerthemtowardtheJavasolutionsincetheirreferencesareJava-heavy.Noticingtheysaid"inC++orJava"-theyseemflexibleonlanguage.ButtheircitationsareallJVMecosystem,soJavaguidanceshouldtakepriority.TheKotlinfactoryexampletheyprovidedmightbeunintentional-couldaskfollow-upaboutthat.Thebiggestriskistheusertryingthisinproductioncode.Mustemphasize"useonlyfortesting"andmentionmodernalternativeslikewrappingtheparser.Shouldpreparerelatedquestionsaboutdependencyinjectionorparseralternativesincasetheyasknext.Wait-whydotheyneedthis?TheconstructoraccessrequestfeelslikeanXYproblem.Maybethey'retryingto:-MockJsonParserforunittests?(suggestPowerMockito)-Forcespecificencoding?(showconfigureParser()alternatives)-Bypassfactoryoverhead?(benchmarkneeded)Theresponseshouldplantseedsforthesefollow-upswhilesolvingtheimmediatequestion.That"§§相关问题§§"sectionwillbekey.</think> 要调用类 `JsonParser` 的私有构造函数,需根据具体语言和上下文采取不同的技术手段。以下是 Java 和 C++ 中的实现方法及注意事项: ### Java 解决方案 1. **通过反射访问** ```java import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import java.lang.reflect.Constructor; public class PrivateConstructorAccess { public static void main(String[] args) throws Exception { // 获取私有构造函数 Constructor<JsonParser> constructor = JsonParser.class .getDeclaredConstructor(JsonFactory.class, int.class); // 解除私有访问限制 constructor.setAccessible(true); // 创建实例(参数根据实际构造函数签名调整) JsonFactory factory = new JsonFactory(); JsonParser parser = constructor.newInstance(factory, 0); System.out.println(parser.isClosed()); // 测试方法调用 } } ``` 2. **通过工厂方法(推荐)** 根据您的引用[1],**标准做法是使用 `JsonFactory.createParser()`**: ```java JsonFactory factory = new JsonFactory(); JsonParser parser = factory.createParser("{\"key\":\"value\"}"); // 官方安全方式[^1] ``` > ⚠️ **重要限制** > - 需添加JVM参数:`--add-opens java.base/java.lang=ALL-UNNAMED`(模块系统限制) > - 违反封装原则,可能导致兼容性问题 --- ### C++ 解决方案(需源码控制) ```cpp // 在需要访问的类中声明友元 class JsonParser { private: JsonParser(); // 私有构造函数 friend class ParserAccessor; // 友元声明 }; // 友元类实现 class ParserAccessor { public: static JsonParser createInstance() { return JsonParser(); // 可直接访问私有构造 } }; int main() { JsonParser parser = ParserAccessor::createInstance(); } ``` > ⚠️ **限制**: > 必须能修改 `JsonParser` 的源码(通常第三方库不可行) --- ### 核心问题与建议 1. **设计冲突** 私有构造函数通常意味着: - 类设计为不可直接实例化(如工具类) - 必须通过工厂方法创建实例(如您的引用[1][^1]和引用[2][^2]所示) - 单例模式等特殊设计约束 2. **推荐替代方案** ```java // 标准创建方式 (Jackson库示例) JsonFactory factory = new JsonFactory(); try (JsonParser parser = factory.createParser(jsonString)) { while (parser.nextToken() != null) { System.out.println(parser.getText()); } } ``` > ✨ **最佳实践**:始终优先使用官方提供的工厂方法(`createParser()`),这能保障: > - 对象初始化完整性 > - 版本兼容性 > - 资源安全管理(如IO流关闭) --- ### 相关问题 1. 为什么 JsonParser 的构造函数通常被设为私有? 2. 如何在单元测试中安全地模拟私有构造函数? 3. JsonFactory.createParser() 底层如何实现对象创建? 4. 违反封装原则调用私有方法会导致什么运行时风险? 5. Java 模块系统(JPMS)如何进一步加强访问控制?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值