1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include <cerrno> 17#include <string> 18#include <vector> 19#include <iostream> 20#include <memory> 21#include <gtest/gtest.h> 22#include <sys/stat.h> 23#include <dirent.h> 24#include <fcntl.h> 25#include <selinux/selinux.h> 26 27#include "param_stub.h" 28#include "ueventd.h" 29#include "ueventd_device_handler.h" 30#include "ueventd_socket.h" 31 32using namespace testing::ext; 33 34namespace UeventdUt { 35namespace { 36 std::string g_testRoot{"/data/ueventd"}; 37 int g_oldRootFd = -1; 38} 39 40class UeventdEventUnitTest : public testing::Test { 41public: 42static void SetUpTestCase(void) 43{ 44 struct stat st{}; 45 bool isExist = true; 46 47 if (stat(g_testRoot.c_str(), &st) < 0) { 48 if (errno != ENOENT) { 49 std::cout << "Cannot get stat of " << g_testRoot << std::endl; 50 // If we cannot get root for ueventd tests 51 // There is no reason to continue. 52 ASSERT_TRUE(false); 53 } 54 isExist = false; 55 } 56 57 if (isExist) { 58 RemoveDir(g_testRoot); 59 } 60 int ret = mkdir(g_testRoot.c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 61 if (ret < 0) { 62 std::cout << "Cannot create directory " << g_testRoot << " err = " << errno << std::endl; 63 ASSERT_TRUE(0); 64 } 65 if (SwitchRoot() < 0) { 66 // If we cannot do this, there is not reason to continue 67 ASSERT_TRUE(0); 68 } 69} 70 71static void TearDownTestCase(void) 72{ 73 // Switch back to old root 74 if (g_oldRootFd < 0) { 75 std::cout << "Old root directory is not valid\n"; 76 return; 77 } 78 79 if (fchdir(g_oldRootFd) < 0) { 80 std::cout << "Failed to change working directory to '/', err = " << errno << std::endl; 81 } 82 83 if (chroot(".") < 0) { 84 std::cout << "Failed to change root directory to '/', err = " << errno << std::endl; 85 } 86 close(g_oldRootFd); 87 g_oldRootFd = -1; 88 std::cout << "Change root back done\n"; 89 // Remove test data 90 if (RemoveDir(g_testRoot) < 0) { 91 std::cout << "Failed to remove " << g_testRoot << ", err = " << errno << std::endl; 92 } 93} 94 95static int RemoveDir(const std::string &path) 96{ 97 if (path.empty()) { 98 // Treat it as OK 99 return 0; 100 } 101 auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir); 102 if (dir == nullptr) { 103 std::cout << "Cannot open dir " << path << ", err = " << errno << std::endl; 104 return -1; 105 } 106 107 struct dirent *dp = nullptr; 108 while ((dp = readdir(dir.get())) != nullptr) { 109 // Skip hidden files 110 if (dp->d_name[0] == '.') { 111 continue; 112 } 113 bool endsWithSlash = (path.find_last_of("/") == path.size() - 1); 114 std::string fullPath {}; 115 if (endsWithSlash) { 116 fullPath = path + dp->d_name; 117 } else { 118 fullPath = path + "/" + dp->d_name; 119 } 120 struct stat st {}; 121 if (stat(fullPath.c_str(), &st) < 0) { 122 std::cout << "Failed to get stat of " << fullPath << std::endl; 123 continue; // Should we continue? 124 } 125 if (S_ISDIR(st.st_mode)) { 126 if (RemoveDir(fullPath) < 0) { 127 std::cout << "Failed to remove directory " << fullPath << std::endl; 128 return -1; 129 } 130 } else { 131 if (unlink(fullPath.c_str()) < 0) { 132 std::cout << "Failed to unlink file " << fullPath << std::endl; 133 return -1; 134 } 135 } 136 } 137 return rmdir(path.c_str()); 138} 139 140static int SwitchRoot() 141{ 142 if (g_oldRootFd >= 0) { 143 close(g_oldRootFd); 144 g_oldRootFd = -1; 145 } 146 // Save old root 147 DIR *dir = opendir("/"); 148 if (dir == nullptr) { 149 std::cout << "Failed to open root directory\n"; 150 return -1; 151 } 152 g_oldRootFd = dirfd(dir); 153 if (g_oldRootFd < 0) { 154 std::cout << "Failed to pen root directory, err = " << errno << std::endl; 155 return -1; 156 } 157 158 // Changing working directory to "/data/ueventd" 159 if (chdir(g_testRoot.c_str()) < 0) { 160 std::cout << "Failed to change working directory to " << g_testRoot << ", err = " << errno << std::endl; 161 close(g_oldRootFd); 162 g_oldRootFd = -1; 163 } 164 165 if (chroot(g_testRoot.c_str()) < 0) { 166 std::cout << "Failed to change root directory to " << g_testRoot << ", err = " << errno << std::endl; 167 close(g_oldRootFd); 168 g_oldRootFd = -1; 169 } 170 std::cout << "Change root to " << g_testRoot << " done\n"; 171 return 0; 172} 173 174void SetUp() {}; 175void TearDown() {}; 176}; 177 178// Generate uevent buffer from struct uevent. 179// extra data used to break uevent buffer to check 180// if ueventd will handle this situation correctly 181std::string GenerateUeventBuffer(struct Uevent &uevent, std::vector<std::string> &extraData) 182{ 183 std::string ueventdBuffer{}; 184 if (uevent.syspath != nullptr) { 185 ueventdBuffer.append(std::string("DEVPATH=") + uevent.syspath + '\000'); 186 } 187 if (uevent.subsystem != nullptr) { 188 ueventdBuffer.append(std::string("SUBSYSTEM=") + uevent.subsystem + '\000'); 189 } 190 ueventdBuffer.append(std::string("ACTION=") + ActionString(uevent.action) + '\000'); 191 if (uevent.deviceName != nullptr) { 192 ueventdBuffer.append(std::string("DEVNAME=") + uevent.deviceName + '\000'); 193 } 194 if (uevent.partitionName != nullptr) { 195 ueventdBuffer.append(std::string("PARTNAME=") + uevent.partitionName + '\000'); 196 } 197 ueventdBuffer.append(std::string("PARTN=") + std::to_string(uevent.partitionNum) + '\000'); 198 ueventdBuffer.append(std::string("MAJOR=") + std::to_string(uevent.major) + '\000'); 199 ueventdBuffer.append(std::string("MINOR=") + std::to_string(uevent.minor) + '\000'); 200 ueventdBuffer.append(std::string("DEVUID=") + std::to_string(uevent.ug.uid) + '\000'); 201 ueventdBuffer.append(std::string("DEVGID=") + std::to_string(uevent.ug.gid) + '\000'); 202 if (uevent.firmware != nullptr) { 203 ueventdBuffer.append(std::string("FIRMWARE=") + uevent.firmware + '\000'); 204 } 205 ueventdBuffer.append(std::string("BUSNUM=") + std::to_string(uevent.busNum) + '\000'); 206 ueventdBuffer.append(std::string("DEVNUM=") + std::to_string(uevent.devNum) + '\000'); 207 208 if (!extraData.empty()) { 209 for (const auto &data : extraData) { 210 ueventdBuffer.append(data); 211 } 212 } 213 return ueventdBuffer; 214} 215 216bool IsFileExist(const std::string &file) 217{ 218 struct stat st{}; 219 if (file.empty()) { 220 return false; 221 } 222 223 if (stat(file.c_str(), &st) < 0) { 224 if (errno == ENOENT) { 225 std::cout << "File " << file << " is not exist\n"; 226 } 227 return false; 228 } 229 return true; 230} 231 232HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_ParseUeventdEvent001, TestSize.Level1) 233{ 234 struct Uevent uevent = { 235 .subsystem = "block", 236 .syspath = "/block/mmc/test", 237 .deviceName = "test", 238 .partitionName = "userdata", 239 .firmware = "", 240 .action = ACTION_ADD, 241 .partitionNum = 3, 242 .major = 1, 243 .minor = 2, 244 .ug = { 245 .uid = 0, 246 .gid = 0, 247 }, 248 .busNum = 1, 249 .devNum = 2, 250 }; 251 252 std::vector<std::string> extraData{}; 253 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData); 254 std::cout << "ueventBuffer = [" << ueventBuffer << "]. size = " << ueventBuffer.length() << std::endl; 255 struct Uevent outEvent; 256 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent); 257 EXPECT_EQ(outEvent.action, ACTION_ADD); 258 EXPECT_EQ(outEvent.busNum, 1); 259 EXPECT_STREQ(outEvent.subsystem, "block"); 260 EXPECT_STREQ(outEvent.deviceName, "test"); 261 EXPECT_EQ(outEvent.devNum, 2); 262 EXPECT_EQ(outEvent.major, 1); 263 EXPECT_EQ(outEvent.minor, 2); 264 EXPECT_EQ(outEvent.partitionNum, 3); 265 EXPECT_STREQ(outEvent.partitionName, "userdata"); 266 EXPECT_STREQ(outEvent.syspath, "/block/mmc/test"); 267 EXPECT_EQ(outEvent.ug.gid, 0); 268 EXPECT_EQ(outEvent.ug.uid, 0); 269 HandleUevent(&outEvent); 270} 271 272HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_Actions001, TestSize.Level1) 273{ 274 struct Uevent uevent = { 275 .subsystem = "block", 276 .syspath = "/block/mmc/test", 277 .deviceName = "test", 278 .partitionName = "userdata", 279 .action = ACTION_UNKNOWN, 280 }; 281 std::vector<std::string> extraData {}; 282 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData); 283 std::cout << "ueventBuffer = [" << ueventBuffer << "]. size = " << ueventBuffer.length() << std::endl; 284 struct Uevent outEvent; 285 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent); 286 EXPECT_EQ(outEvent.action, ACTION_UNKNOWN); 287 HandleUevent(&outEvent); 288} 289 290HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesInvalidParameters001, TestSize.Level1) 291{ 292 HandleBlockDeviceEvent(nullptr); 293 // Not block device 294 struct Uevent noBlockUevent = { 295 .subsystem = "char", 296 }; 297 HandleBlockDeviceEvent(&noBlockUevent); 298 299 struct Uevent invalidDevNoUevent = { 300 .subsystem = "block", 301 .major = -1, 302 .minor = -1, 303 }; 304 HandleBlockDeviceEvent(&invalidDevNoUevent); 305 306 struct Uevent invalidSysPathUevent = { 307 .subsystem = "block", 308 .syspath = nullptr, 309 .major = 1, 310 .minor = 1, 311 }; 312 HandleBlockDeviceEvent(&invalidSysPathUevent); 313} 314 315HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesValidParameters002, TestSize.Level1) 316{ 317 struct Uevent uevent = { 318 .subsystem = "block", 319 .syspath = "/block/mmc/block_device_test", 320 .deviceName = "block_device_test", 321 .partitionName = "block_device_test", 322 .firmware = "", 323 .action = ACTION_ADD, 324 .partitionNum = 3, 325 .major = 5, 326 .minor = 15, 327 .ug = { 328 .uid = 0, 329 .gid = 0, 330 }, 331 .busNum = 1, 332 .devNum = 2, 333 }; 334 335 HandleBlockDeviceEvent(&uevent); 336 // Check results 337 std::string blockDevice = "/dev/block/block_device_test"; 338 struct stat st{}; 339 int ret = stat(blockDevice.c_str(), &st); 340 EXPECT_EQ(ret, 0); 341 bool isBlock = S_ISBLK(st.st_mode); 342 EXPECT_TRUE(isBlock); 343} 344 345HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesRemoved001, TestSize.Level1) 346{ 347 struct Uevent uevent = { 348 .subsystem = "block", 349 .syspath = "/block/mmc/block_device_test", 350 .deviceName = "block_device_test", 351 .partitionName = "block_device_test", 352 .firmware = "", 353 .action = ACTION_REMOVE, 354 .partitionNum = 3, 355 .major = 5, 356 .minor = 15, 357 .ug = { 358 .uid = 0, 359 .gid = 0, 360 }, 361 .busNum = 1, 362 .devNum = 2, 363 }; 364 std::string blockDevice = "/dev/block/block_device_test"; 365 struct stat st{}; 366 int ret = stat(blockDevice.c_str(), &st); 367 if (ret < 0) { 368 // This should not happen actually, because we've created the device node before. 369 std::cout << "Warning. Block device " << blockDevice << " is not exist.\n"; 370 } 371 HandleBlockDeviceEvent(&uevent); 372 ret = stat(blockDevice.c_str(), &st); 373 EXPECT_EQ(ret, -1); 374 EXPECT_EQ(errno, ENOENT); 375} 376 377HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleBlockDevicesChanged001, TestSize.Level1) 378{ 379 struct Uevent uevent = { 380 .subsystem = "block", 381 .syspath = "/block/mmc/block_device_test", 382 .deviceName = "block_device_test", 383 .partitionName = "block_device_test", 384 .firmware = "", 385 .action = ACTION_REMOVE, 386 .partitionNum = 3, 387 .major = 5, 388 .minor = 15, 389 .ug = { 390 .uid = 0, 391 .gid = 0, 392 }, 393 .busNum = 1, 394 .devNum = 2, 395 }; 396 HandleBlockDeviceEvent(&uevent); 397} 398 399HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleOtherDevicesInvalidParameters001, TestSize.Level1) 400{ 401 HandleOtherDeviceEvent(nullptr); 402 // Not Character device 403 struct Uevent invalidDevNoUevent = { 404 .subsystem = "test", 405 .major = -1, 406 .minor = -1, 407 }; 408 HandleOtherDeviceEvent(&invalidDevNoUevent); 409 410 struct Uevent invalidSysPathUevent = { 411 .subsystem = "test", 412 .syspath = nullptr, 413 .major = 5, 414 .minor = 9, 415 }; 416 HandleOtherDeviceEvent(&invalidSysPathUevent); 417 418 struct Uevent invalidSubsystemUevent = { 419 .subsystem = "", 420 .syspath = "/devices/test/char", 421 .major = 5, 422 .minor = 9, 423 }; 424 HandleOtherDeviceEvent(&invalidSubsystemUevent); 425} 426 427HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleOtherDevicesValidParameters001, TestSize.Level1) 428{ 429 struct Uevent uevent = { 430 .subsystem = "extcon3", 431 .syspath = "/devices/platform/headset/extcon/extcon3", 432 .deviceName = "extcon3-1", 433 .major = 5, 434 .minor = 9, 435 }; 436 HandleOtherDeviceEvent(&uevent); 437 auto exist = IsFileExist("/dev/extcon3-1"); 438 EXPECT_TRUE(exist); 439 exist = IsFileExist("/dev/extcon3"); 440 EXPECT_FALSE(exist); 441} 442 443HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleUsbDevicesWithDeviceName001, TestSize.Level1) 444{ 445 struct Uevent uevent = { 446 .subsystem = "usb", 447 .syspath = "/devices/platform/headset/extcon/usb-dev", 448 .deviceName = "usb-dev", 449 .major = 8, 450 .minor = 9, 451 }; 452 HandleOtherDeviceEvent(&uevent); 453 auto exist = IsFileExist("/dev/usb-dev"); 454 EXPECT_TRUE(exist); 455} 456 457HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleInvalidUsbDevices001, TestSize.Level1) 458{ 459 struct Uevent uevent = { 460 .subsystem = "usb", 461 .syspath = "/devices/platform/headset/extcon/usb-dev-1", 462 .major = 8, 463 .minor = 10, 464 .busNum = -1, 465 .devNum = -1, 466 }; 467 HandleOtherDeviceEvent(&uevent); 468 auto exist = IsFileExist("/dev/usb-dev-1"); 469 EXPECT_FALSE(exist); 470} 471 472HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_HandleUsbDevicesWithBusNo001, TestSize.Level1) 473{ 474 struct Uevent uevent = { 475 .subsystem = "usb", 476 .syspath = "/devices/platform/headset/extcon/usb-dev", 477 .major = 8, 478 .minor = 9, 479 .busNum = 3, 480 .devNum = 4, 481 }; 482 HandleOtherDeviceEvent(&uevent); 483 auto exist = IsFileExist("/dev/bus/usb/003/004"); 484 EXPECT_TRUE(exist); 485} 486 487HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_Handle001, TestSize.Level1) 488{ 489 char path[] = {"/data/ueventd"}; 490 RetriggerUeventByPath(g_oldRootFd, path); 491} 492 493HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_FirmwareUevent001, TestSize.Level1) 494{ 495 struct Uevent uevent = { 496 .subsystem = "firmware", 497 .syspath = "/block/mmc/test", 498 .deviceName = "test", 499 .partitionName = "userdata", 500 .firmware = "", 501 .action = ACTION_ADD, 502 .partitionNum = 3, 503 .major = 1, 504 .minor = 2, 505 .ug = { 506 .uid = 0, 507 .gid = 0, 508 }, 509 .busNum = 1, 510 .devNum = 2, 511 }; 512 513 std::vector<std::string> extraData{}; 514 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData); 515 struct Uevent outEvent; 516 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent); 517 EXPECT_EQ(outEvent.action, ACTION_ADD); 518 EXPECT_EQ(outEvent.busNum, 1); 519 EXPECT_STREQ(outEvent.subsystem, "firmware"); 520 EXPECT_STREQ(outEvent.deviceName, "test"); 521 EXPECT_EQ(outEvent.devNum, 2); 522 EXPECT_EQ(outEvent.major, 1); 523 EXPECT_EQ(outEvent.minor, 2); 524 EXPECT_EQ(outEvent.partitionNum, 3); 525 EXPECT_STREQ(outEvent.partitionName, "userdata"); 526 EXPECT_STREQ(outEvent.syspath, "/block/mmc/test"); 527 EXPECT_EQ(outEvent.ug.gid, 0); 528 EXPECT_EQ(outEvent.ug.uid, 0); 529 HandleUevent(&outEvent); 530} 531 532HWTEST_F(UeventdEventUnitTest, Init_UeventdEventTest_PlatformEvent001, TestSize.Level1) 533{ 534 struct Uevent uevent = { 535 .subsystem = "platform", 536 .syspath = "/block/mmc/test", 537 .deviceName = "test", 538 .partitionName = "userdata", 539 .firmware = "", 540 .action = ACTION_ADD, 541 .partitionNum = 3, 542 .major = 1, 543 .minor = 2, 544 .ug = { 545 .uid = 0, 546 .gid = 0, 547 }, 548 .busNum = 1, 549 .devNum = 2, 550 }; 551 552 std::vector<std::string> extraData{}; 553 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData); 554 struct Uevent outEvent; 555 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent); 556 EXPECT_EQ(outEvent.action, ACTION_ADD); 557 EXPECT_EQ(outEvent.busNum, 1); 558 EXPECT_STREQ(outEvent.subsystem, "platform"); 559 EXPECT_STREQ(outEvent.deviceName, "test"); 560 EXPECT_EQ(outEvent.devNum, 2); 561 EXPECT_EQ(outEvent.major, 1); 562 EXPECT_EQ(outEvent.minor, 2); 563 EXPECT_EQ(outEvent.partitionNum, 3); 564 EXPECT_STREQ(outEvent.partitionName, "userdata"); 565 EXPECT_STREQ(outEvent.syspath, "/block/mmc/test"); 566 EXPECT_EQ(outEvent.ug.gid, 0); 567 EXPECT_EQ(outEvent.ug.uid, 0); 568 HandleUevent(&outEvent); 569} 570 571HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_PlatformEventUsb001, TestSize.Level1) 572{ 573 struct Uevent uevent = { 574 .subsystem = "usb", 575 .syspath = "/block/mmc/test", 576 .deviceName = "test", 577 .partitionName = "userdata", 578 .firmware = "", 579 .action = ACTION_ADD, 580 .partitionNum = 3, 581 .major = 1, 582 .minor = 2, 583 .ug = { 584 .uid = 0, 585 .gid = 0, 586 }, 587 .busNum = 1, 588 .devNum = 2, 589 }; 590 591 std::vector<std::string> extraData{}; 592 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData); 593 struct Uevent outEvent; 594 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent); 595 EXPECT_EQ(outEvent.action, ACTION_ADD); 596 EXPECT_EQ(outEvent.busNum, 1); 597 EXPECT_STREQ(outEvent.subsystem, "usb"); 598 EXPECT_STREQ(outEvent.deviceName, "test"); 599 EXPECT_EQ(outEvent.devNum, 2); 600 EXPECT_EQ(outEvent.major, 1); 601 EXPECT_EQ(outEvent.minor, 2); 602 EXPECT_EQ(outEvent.partitionNum, 3); 603 EXPECT_STREQ(outEvent.partitionName, "userdata"); 604 EXPECT_STREQ(outEvent.syspath, "/block/mmc/test"); 605 EXPECT_EQ(outEvent.ug.gid, 0); 606 EXPECT_EQ(outEvent.ug.uid, 0); 607 HandleUevent(&outEvent); 608} 609 610static void TestUeventAction(ACTION action) 611{ 612 struct Uevent uevent = { 613 .subsystem = "block", 614 .syspath = "/block/mmc/test", 615 .deviceName = "test", 616 .partitionName = "userdata", 617 .firmware = "", 618 .action = action, 619 .partitionNum = 3, 620 .major = 1, 621 .minor = 2, 622 .ug = { 623 .uid = 0, 624 .gid = 0, 625 }, 626 .busNum = 1, 627 .devNum = 2, 628 }; 629 630 std::vector<std::string> extraData{}; 631 auto ueventBuffer = GenerateUeventBuffer(uevent, extraData); 632 struct Uevent outEvent; 633 ParseUeventMessage(ueventBuffer.data(), ueventBuffer.length(), &outEvent); 634 EXPECT_EQ(outEvent.action, action); 635 HandleUevent(&outEvent); 636} 637 638HWTEST_F(UeventdEventUnitTest, Init_UeventdEventUnitTest_ActionAdd001, TestSize.Level1) 639{ 640 TestUeventAction(ACTION_ADD); 641 TestUeventAction(ACTION_REMOVE); 642 TestUeventAction(ACTION_CHANGE); 643 TestUeventAction(ACTION_MOVE); 644 TestUeventAction(ACTION_ONLINE); 645 646 TestUeventAction(ACTION_OFFLINE); 647 TestUeventAction(ACTION_BIND); 648 TestUeventAction(ACTION_UNBIND); 649 TestUeventAction(ACTION_UNKNOWN); 650} 651} // UeventdUt