1 /* 2 * Copyright (C) 2008 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 #ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ 18 #define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ 19 20 #include <time.h> 21 #include <unistd.h> 22 #include <stdbool.h> 23 #include <stdio.h> 24 25 #include <string> 26 #include <vector> 27 28 #include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> 29 #include <android-base/macros.h> 30 #include <android-base/unique_fd.h> 31 #include <android/hardware/dumpstate/1.1/types.h> 32 #include <android/os/BnIncidentAuthListener.h> 33 #include <android/os/IDumpstate.h> 34 #include <android/os/IDumpstateListener.h> 35 #include <utils/StrongPointer.h> 36 #include <ziparchive/zip_writer.h> 37 38 #include "DumpstateUtil.h" 39 #include "DumpPool.h" 40 #include "TaskQueue.h" 41 42 // TODO: move everything under this namespace 43 // TODO: and then remove explicitly android::os::dumpstate:: prefixes 44 namespace android { 45 namespace os { 46 47 struct DumpstateOptions; 48 49 namespace dumpstate { 50 51 class DumpstateTest; 52 class ProgressTest; 53 class ZippedBugReportStreamTest; 54 55 } // namespace dumpstate 56 } // namespace os 57 } // namespace android 58 59 class ZipWriter; 60 61 // TODO: remove once moved to HAL 62 #ifdef __cplusplus 63 extern "C" { 64 #endif 65 66 /* 67 * Helper class used to report how long it takes for a section to finish. 68 * 69 * Typical usage: 70 * 71 * DurationReporter duration_reporter(title); 72 * 73 */ 74 class DurationReporter { 75 public: 76 explicit DurationReporter(const std::string& title, bool logcat_only = false, 77 bool verbose = false, int duration_fd = STDOUT_FILENO); 78 79 ~DurationReporter(); 80 81 private: 82 std::string title_; 83 bool logcat_only_; 84 bool verbose_; 85 uint64_t started_; 86 int duration_fd_; 87 88 DISALLOW_COPY_AND_ASSIGN(DurationReporter); 89 }; 90 91 /* 92 * Keeps track of current progress and estimated max, saving stats on file to tune up future runs. 93 * 94 * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall 95 * progress can be calculated by dividing the estimate max progress by the current progress. 96 * 97 * The estimated max progress is initially set to a value (`kDefaultMax) defined empirically, but 98 * it's adjusted after each dumpstate run by storing the average duration in a file. 99 * 100 */ 101 class Progress { 102 friend class android::os::dumpstate::ProgressTest; 103 friend class android::os::dumpstate::DumpstateTest; 104 105 public: 106 /* 107 * Default estimation of the max duration of a bugreport generation. 108 * 109 * It does not need to match the exact sum of all sections, but ideally it should to be slight 110 * more than such sum: a value too high will cause the bugreport to finish before the user 111 * expected (for example, jumping from 70% to 100%), while a value too low will cause the 112 * progress to get stuck at an almost-finished value (like 99%) for a while. 113 * 114 * This constant is only used when the average duration from previous runs cannot be used. 115 */ 116 static const int kDefaultMax; 117 118 explicit Progress(const std::string& path = ""); 119 120 // Gets the current progress. 121 int32_t Get() const; 122 123 // Gets the current estimated max progress. 124 int32_t GetMax() const; 125 126 // Gets the initial estimated max progress. 127 int32_t GetInitialMax() const; 128 129 // Increments progress (ignored if not positive). 130 // Returns `true` if the max progress increased as well. 131 bool Inc(int32_t delta); 132 133 // Persist the stats. 134 void Save(); 135 136 void Dump(int fd, const std::string& prefix) const; 137 138 private: 139 Progress(int32_t initial_max, float growth_factor, 140 const std::string& path = ""); // Used by test cases. 141 Progress(int32_t initial_max, int32_t progress, float growth_factor); // Used by test cases. 142 void Load(); 143 int32_t initial_max_; 144 int32_t progress_; 145 int32_t max_; 146 float growth_factor_; 147 int32_t n_runs_; 148 int32_t average_max_; 149 std::string path_; 150 }; 151 152 /* 153 * List of supported zip format versions. 154 * 155 * See bugreport-format.md for more info. 156 */ 157 static std::string VERSION_CURRENT = "2.0"; 158 159 /* 160 * "Alias" for the current version. 161 */ 162 static std::string VERSION_DEFAULT = "default"; 163 164 /* 165 * Directory used by Dumpstate binary to keep its local files. 166 */ 167 static const std::string DUMPSTATE_DIRECTORY = "/bugreports"; 168 169 /* 170 * Structure that contains the information of an open dump file. 171 */ 172 struct DumpData { 173 // Path of the file. 174 std::string name; 175 176 // Open file descriptor for the file. 177 android::base::unique_fd fd; 178 179 // Modification time of the file. 180 time_t mtime; 181 }; 182 183 /* 184 * Main class driving a bugreport generation. 185 * 186 * Currently, it only contains variables that are accessed externally, but gradually the functions 187 * that are spread accross utils.cpp and dumpstate.cpp will be moved to it. 188 */ 189 class Dumpstate { 190 friend class android::os::dumpstate::DumpstateTest; 191 friend class android::os::dumpstate::ZippedBugReportStreamTest; 192 193 public: 194 enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT }; 195 196 // The mode under which the bugreport should be run. Each mode encapsulates a few options. 197 enum BugreportMode { 198 BUGREPORT_FULL = android::os::IDumpstate::BUGREPORT_MODE_FULL, 199 BUGREPORT_INTERACTIVE = android::os::IDumpstate::BUGREPORT_MODE_INTERACTIVE, 200 BUGREPORT_REMOTE = android::os::IDumpstate::BUGREPORT_MODE_REMOTE, 201 BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR, 202 BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY, 203 BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI, 204 BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT 205 }; 206 207 // The flags used to customize bugreport requests. 208 enum BugreportFlag { 209 BUGREPORT_USE_PREDUMPED_UI_DATA = 210 android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA, 211 BUGREPORT_FLAG_DEFER_CONSENT = 212 android::os::IDumpstate::BUGREPORT_FLAG_DEFER_CONSENT 213 }; 214 215 static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS; 216 217 static Dumpstate& GetInstance(); 218 219 /* Initialize dumpstate fields before starting bugreport generation */ 220 void Initialize(); 221 222 /* 223 * Forks a command, waits for it to finish, and returns its status. 224 * 225 * |title| description of the command printed on `stdout` (or empty to skip 226 * description). 227 * |full_command| array containing the command (first entry) and its arguments. 228 * Must contain at least one element. 229 * |options| optional argument defining the command's behavior. 230 * |out_fd| A fd to support the DumpPool to output results to a temporary 231 * file. Using STDOUT_FILENO if it's not running in the parallel task. 232 */ 233 int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, 234 const android::os::dumpstate::CommandOptions& options = 235 android::os::dumpstate::CommandOptions::DEFAULT, 236 bool verbose_duration = false, int out_fd = STDOUT_FILENO); 237 238 /* 239 * Runs `dumpsys` with the given arguments, automatically setting its timeout 240 * (`-T` argument) 241 * according to the command options. 242 * 243 * |title| description of the command printed on `stdout` (or empty to skip 244 * description). 245 * |dumpsys_args| `dumpsys` arguments (except `-t`). 246 * |options| optional argument defining the command's behavior. 247 * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the 248 * timeout from `options`) 249 * |out_fd| A fd to support the DumpPool to output results to a temporary 250 * file. Using STDOUT_FILENO if it's not running in the parallel task. 251 */ 252 void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, 253 const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS, 254 long dumpsys_timeout_ms = 0, int out_fd = STDOUT_FILENO); 255 256 /* 257 * Prints the contents of a file. 258 * 259 * |title| description of the command printed on `stdout` (or empty to skip 260 * description). 261 * |path| location of the file to be dumped. 262 */ 263 int DumpFile(const std::string& title, const std::string& path); 264 265 /* 266 * Adds a new entry to the existing zip file. 267 * */ 268 bool AddZipEntry(const std::string& entry_name, const std::string& entry_path); 269 270 /* 271 * Adds a new entry to the existing zip file. 272 * 273 * |entry_name| destination path of the new entry. 274 * |fd| file descriptor to read from. 275 * |timeout| timeout to terminate the read if not completed. Set 276 * value of 0s (default) to disable timeout. 277 */ 278 android::status_t AddZipEntryFromFd(const std::string& entry_name, int fd, 279 std::chrono::milliseconds timeout); 280 281 /* 282 * Adds a text entry to the existing zip file. 283 */ 284 bool AddTextZipEntry(const std::string& entry_name, const std::string& content); 285 286 /* 287 * Adds all files from a directory to the zipped bugreport file. 288 */ 289 void AddDir(const std::string& dir, bool recursive); 290 291 /* 292 * Takes a screenshot and save it to the given `path`. 293 * 294 * If `path` is empty, uses a standard path based on the bugreport name. 295 */ 296 void TakeScreenshot(const std::string& path = ""); 297 298 ///////////////////////////////////////////////////////////////////// 299 // TODO: members below should be private once refactor is finished // 300 ///////////////////////////////////////////////////////////////////// 301 302 // TODO: temporary method until Dumpstate object is properly set 303 void SetProgress(std::unique_ptr<Progress> progress); 304 305 // Dumps Dalvik and native stack traces, sets the trace file location to path 306 // if it succeeded. 307 // Note that it returns early if user consent is denied with status USER_CONSENT_DENIED. 308 // Returns OK in all other cases. 309 RunStatus DumpTraces(const char** path); 310 311 /* 312 * |out_fd| A fd to support the DumpPool to output results to a temporary file. 313 * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO 314 * if it's not running in the parallel task. 315 */ 316 void DumpstateBoard(int out_fd = STDOUT_FILENO); 317 318 /* 319 * Updates the overall progress of the bugreport generation by the given weight increment. 320 */ 321 void UpdateProgress(int32_t delta); 322 323 /* Prints the dumpstate header on `stdout`. */ 324 void PrintHeader() const; 325 326 /* 327 * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the 328 * temporary file. 329 */ 330 bool FinishZipFile(); 331 332 /* Constructs a full path inside directory with file name formatted using the given suffix. */ 333 std::string GetPath(const std::string& directory, const std::string& suffix) const; 334 335 /* Constructs a full path inside bugreport_internal_dir_ with file name formatted using the 336 * given suffix. */ 337 std::string GetPath(const std::string& suffix) const; 338 339 /* Returns true if the current version supports priority dump feature. */ 340 bool CurrentVersionSupportsPriorityDumps() const; 341 342 struct DumpOptions; 343 344 /** 345 * Pre-dump critical UI data, e.g. data stored in short ring buffers that might get lost 346 * by the time the actual bugreport is requested. 347 */ 348 void PreDumpUiData(); 349 350 /* 351 * Main entry point for running a complete bugreport. 352 * 353 * Initialize() dumpstate before calling this method. 354 * 355 */ 356 RunStatus Run(int32_t calling_uid, const std::string& calling_package); 357 358 /* 359 * Entry point for retrieving a previous-generated bugreport. 360 * 361 * Initialize() dumpstate before calling this method. 362 */ 363 RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package); 364 365 366 367 RunStatus ParseCommandlineAndRun(int argc, char* argv[]); 368 369 /* Deletes in-progress files */ 370 void Cancel(); 371 372 /* Sets runtime options. */ 373 void SetOptions(std::unique_ptr<DumpOptions> options); 374 375 /* 376 * Returns true if user consent is necessary and has been denied. 377 * Consent is only necessary if the caller has asked to copy over the bugreport to a file they 378 * provided. 379 */ 380 bool IsUserConsentDenied() const; 381 382 /* 383 * Returns true if dumpstate is called by bugreporting API 384 */ 385 bool CalledByApi() const; 386 387 /* 388 * Enqueues a task to the dumpstate's TaskQueue if the parallel run is enabled, 389 * otherwise invokes it immediately. The task adds file at path entry_path 390 * as a zip file entry with name entry_name. Unlinks entry_path when done. 391 * 392 * All enqueued tasks will be executed in the dumpstate's FinishZipFile method 393 * before the zip file is finished. Tasks will be cancelled in dumpstate's 394 * ShutdownDumpPool method if they have never been called. 395 */ 396 void EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name, 397 const std::string& entry_path); 398 399 /* 400 * Structure to hold options that determine the behavior of dumpstate. 401 */ 402 struct DumpOptions { 403 bool do_vibrate = true; 404 // Writes bugreport zipped file to a socket. 405 bool stream_to_socket = false; 406 // Writes generation progress updates to a socket. 407 bool progress_updates_to_socket = false; 408 bool do_screenshot = false; 409 bool is_screenshot_copied = false; 410 bool is_consent_deferred = false; 411 bool is_remote_mode = false; 412 bool show_header_only = false; 413 bool telephony_only = false; 414 bool wifi_only = false; 415 // Trimmed-down version of dumpstate to only include whitelisted logs. 416 bool limited_only = false; 417 // Whether progress updates should be published. 418 bool do_progress_updates = false; 419 // this is used to derive dumpstate HAL bug report mode 420 // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead. 421 // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide). 422 BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; 423 // Will use data collected through a previous call to PreDumpUiData(). 424 bool use_predumped_ui_data; 425 // File descriptor to output zip file. Takes precedence over out_dir. 426 android::base::unique_fd bugreport_fd; 427 // File descriptor to screenshot file. 428 android::base::unique_fd screenshot_fd; 429 // Custom output directory. 430 std::string out_dir; 431 // Bugreport mode of the bugreport as a string 432 std::string bugreport_mode_string; 433 // Command-line arguments as string 434 std::string args; 435 // Notification title and description 436 std::string notification_title; 437 std::string notification_description; 438 439 /* Initializes options from commandline arguments. */ 440 RunStatus Initialize(int argc, char* argv[]); 441 442 /* Initializes options from the requested mode. */ 443 void Initialize(BugreportMode bugreport_mode, int bugreport_flags, 444 const android::base::unique_fd& bugreport_fd, 445 const android::base::unique_fd& screenshot_fd, 446 bool is_screenshot_requested); 447 448 /* Returns true if the options set so far are consistent. */ 449 bool ValidateOptions() const; 450 451 /* Returns if options specified require writing to custom file location */ OutputToCustomFileDumpOptions452 bool OutputToCustomFile() { 453 // Custom location is only honored in limited mode. 454 return limited_only && !out_dir.empty() && bugreport_fd.get() == -1; 455 } 456 }; 457 458 // TODO: initialize fields on constructor 459 // dumpstate id - unique after each device reboot. 460 uint32_t id_; 461 462 // dumpstate pid 463 pid_t pid_; 464 465 // Runtime options. 466 std::unique_ptr<DumpOptions> options_; 467 468 // Last progress that was sent to the listener [0-100]. 469 int last_reported_percent_progress_ = 0; 470 471 // Whether it should take an screenshot earlier in the process. 472 bool do_early_screenshot_ = false; 473 474 // This is set to true when the trace snapshot request in the early call to 475 // MaybeSnapshotSystemTrace(). When this is true, the later stages of 476 // dumpstate will append the trace to the zip archive. 477 bool has_system_trace_ = false; 478 479 std::unique_ptr<Progress> progress_; 480 481 // When set, defines a socket file-descriptor use to report progress to bugreportz 482 // or to stream the zipped file to. 483 int control_socket_fd_ = -1; 484 485 // Bugreport format version; 486 std::string version_ = VERSION_CURRENT; 487 488 time_t now_; 489 490 // Base name (without suffix or extensions) of the bugreport files, typically 491 // `bugreport-BUILD_ID`. 492 std::string base_name_; 493 494 // Name is the suffix part of the bugreport files - it's typically the date, 495 // but it could be changed by the user.. 496 std::string name_; 497 498 std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; 499 500 // Full path of the temporary file containing the bugreport, inside bugreport_internal_dir_. 501 // At the very end this file is pulled into the zip file. 502 std::string tmp_path_; 503 504 // Full path of the file containing the dumpstate logs, inside bugreport_internal_dir_. 505 // This is useful for debugging. 506 std::string log_path_; 507 508 // Full path of the bugreport zip file inside bugreport_internal_dir_. 509 std::string path_; 510 511 // Full path of the file containing the screenshot (when requested). 512 std::string screenshot_path_; 513 514 // Pointer to the zipped file. 515 std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose}; 516 517 // Pointer to the zip structure. 518 std::unique_ptr<ZipWriter> zip_writer_; 519 520 // Binder object listening to progress. 521 android::sp<android::os::IDumpstateListener> listener_; 522 523 // List of open tombstone dump files. 524 std::vector<DumpData> tombstone_data_; 525 526 // List of open ANR dump files. 527 std::vector<DumpData> anr_data_; 528 529 // List of open shutdown checkpoint files. 530 std::vector<DumpData> shutdown_checkpoints_; 531 532 // A thread pool to execute dump tasks simultaneously if the parallel run is enabled. 533 std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_; 534 535 // A task queue to collect adding zip entry tasks inside dump tasks if the 536 // parallel run is enabled. 537 std::unique_ptr<android::os::dumpstate::TaskQueue> zip_entry_tasks_; 538 539 // A callback to IncidentCompanion service, which checks user consent for sharing the 540 // bugreport with the calling app. If the user has not responded yet to the dialog it will 541 // be neither confirmed nor denied. 542 class ConsentCallback : public android::os::BnIncidentAuthListener { 543 public: 544 ConsentCallback(); 545 android::binder::Status onReportApproved() override; 546 android::binder::Status onReportDenied() override; 547 548 enum ConsentResult { APPROVED, DENIED, UNAVAILABLE }; 549 550 ConsentResult getResult(); 551 552 // Returns the time since creating this listener 553 uint64_t getElapsedTimeMs() const; 554 555 private: 556 ConsentResult result_; 557 uint64_t start_time_; 558 std::mutex lock_; 559 }; 560 561 private: 562 RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package); 563 RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package); 564 565 RunStatus DumpstateDefaultAfterCritical(); 566 RunStatus dumpstate(); 567 568 void MaybeTakeEarlyScreenshot(); 569 void MaybeSnapshotSystemTrace(); 570 void MaybeSnapshotUiTraces(); 571 void MaybePostProcessUiTraces(); 572 void MaybeAddUiTracesToZip(); 573 574 void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid); 575 576 void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package); 577 578 // Removes the in progress files output files (tmp file, zip/txt file, screenshot), 579 // but leaves the log file alone. 580 void CleanupTmpFiles(); 581 582 // Create the thread pool to enable the parallel run function. 583 void EnableParallelRunIfNeeded(); 584 void ShutdownDumpPool(); 585 586 RunStatus HandleUserConsentDenied(); 587 588 void HandleRunStatus(RunStatus status); 589 590 // Copies bugreport artifacts over to the caller's directories provided there is user consent or 591 // called by Shell. 592 RunStatus CopyBugreportIfUserConsented(int32_t calling_uid); 593 594 std::function<int(const char *)> open_socket_fn_; 595 596 // Used by GetInstance() only. 597 explicit Dumpstate(const std::string& version = VERSION_CURRENT); 598 599 android::sp<ConsentCallback> consent_callback_; 600 601 std::recursive_mutex mutex_; 602 603 DISALLOW_COPY_AND_ASSIGN(Dumpstate); 604 }; 605 606 // for_each_pid_func = void (*)(int, const char*); 607 // for_each_tid_func = void (*)(int, int, const char*); 608 609 typedef void(for_each_pid_func)(int, const char*); 610 typedef void(for_each_tid_func)(int, int, const char*); 611 612 /* saves the the contents of a file as a long */ 613 int read_file_as_long(const char *path, long int *output); 614 615 /* prints the contents of the fd 616 * fd must have been opened with the flag O_NONBLOCK. 617 */ 618 int dump_file_from_fd(const char *title, const char *path, int fd); 619 620 /* calls skip to gate calling dump_from_fd recursively 621 * in the specified directory. dump_from_fd defaults to 622 * dump_file_from_fd above when set to NULL. skip defaults 623 * to false when set to NULL. dump_from_fd will always be 624 * called with title NULL. 625 */ 626 int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), 627 int (*dump_from_fd)(const char* title, const char* path, int fd)); 628 629 /* 630 * Redirects 'redirect' to a file indicated by 'path', truncating it. 631 * 632 * Returns true if redirect succeeds. 633 */ 634 bool redirect_to_file(FILE* redirect, char* path); 635 636 /* 637 * Redirects 'redirect' to an existing file indicated by 'path', appending it. 638 * 639 * Returns true if redirect succeeds. 640 */ 641 bool redirect_to_existing_file(FILE* redirect, char* path); 642 643 /* create leading directories, if necessary */ 644 void create_parent_dirs(const char *path); 645 646 /* for each process in the system, run the specified function */ 647 void for_each_pid(for_each_pid_func func, const char *header); 648 649 /* for each thread in the system, run the specified function */ 650 void for_each_tid(for_each_tid_func func, const char *header); 651 652 /* Displays a blocked processes in-kernel wait channel */ 653 void show_wchan(int pid, int tid, const char *name); 654 655 /* Displays a processes times */ 656 void show_showtime(int pid, const char *name); 657 658 /* Gets the dmesg output for the kernel */ 659 void do_dmesg(); 660 661 /* Prints the contents of all the routing tables, both IPv4 and IPv6. */ 662 void dump_route_tables(); 663 664 /* Dump subdirectories of cgroupfs if the corresponding process is frozen */ 665 void dump_frozen_cgroupfs(); 666 667 /* Play a sound via Stagefright */ 668 void play_sound(const char *path); 669 670 /* Checks if a given path is a directory. */ 671 bool is_dir(const char* pathname); 672 673 /** Gets the last modification time of a file, or default time if file is not found. */ 674 time_t get_mtime(int fd, time_t default_mtime); 675 676 /** Gets command-line arguments. */ 677 void format_args(int argc, const char *argv[], std::string *args); 678 679 /** Main entry point for dumpstate. */ 680 int run_main(int argc, char* argv[]); 681 682 #ifdef __cplusplus 683 } 684 #endif 685 686 #endif /* FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ */ 687